| // portable acid for all architectures | 
 |  | 
 | defn pfl(addr) | 
 | { | 
 | 	print(pcfile(addr), ":", pcline(addr), "\n"); | 
 | } | 
 |  | 
 | defn | 
 | notestk(addr) | 
 | { | 
 | 	local pc, sp; | 
 | 	complex Ureg addr; | 
 |  | 
 | 	pc = addr.pc\X; | 
 | 	sp = addr.sp\X; | 
 |  | 
 | 	print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); | 
 | 	pfl(pc); | 
 | 	_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); | 
 | } | 
 |  | 
 | defn | 
 | notelstk(addr) | 
 | { | 
 | 	local pc, sp; | 
 | 	complex Ureg addr; | 
 |  | 
 | 	pc = addr.pc\X; | 
 | 	sp = addr.sp\X; | 
 |  | 
 | 	print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); | 
 | 	pfl(pc); | 
 | 	_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); | 
 | } | 
 |  | 
 | defn params(param) | 
 | { | 
 | 	while param do { | 
 | 		sym = head param; | 
 | 		print(sym[0], "=", itoa(sym[1], "%#ux")); | 
 | 		param = tail param; | 
 | 		if param then | 
 | 			print (","); | 
 | 	}	 | 
 | } | 
 |  | 
 | stkprefix = ""; | 
 | stkignore = {}; | 
 | stkend = 0; | 
 |  | 
 | defn locals(l) | 
 | { | 
 | 	local sym; | 
 |  | 
 | 	while l do { | 
 | 		sym = head l; | 
 | 		print(stkprefix, "\t", sym[0], "=", itoa(sym[1], "%#ux"), "\n"); | 
 | 		l = tail l; | 
 | 	}	 | 
 | } | 
 |  | 
 | defn _stkign(frame) | 
 | { | 
 | 	local file; | 
 |  | 
 | 	file = pcfile(frame[0]); | 
 | 	s = stkignore; | 
 | 	while s do { | 
 | 		if regexp(head s, file) then | 
 | 			return 1; | 
 | 		s = tail s; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | // print a stack trace | 
 | // | 
 | // in a run of leading frames in files matched by regexps in stkignore, | 
 | // only print the last one. | 
 | defn _stk(regs, dolocals) | 
 | { | 
 | 	local stk, frame, pc, fn, done, callerpc, paramlist, locallist; | 
 |  | 
 | 	stk = strace(regs); | 
 | 	if stkignore then { | 
 | 		while stk && tail stk && _stkign(head tail stk) do | 
 | 			stk = tail stk; | 
 | 	} | 
 |  | 
 | 	callerpc = 0; | 
 | 	done = 0; | 
 | 	while stk && !done do { | 
 | 		frame = head stk; | 
 | 		stk = tail stk; | 
 | 		fn = frame[0]; | 
 | 		pc = frame[1]; | 
 | 		callerpc = frame[2]; | 
 | 		paramlist = frame[3]; | 
 | 		locallist = frame[4]; | 
 |  | 
 | 		print(stkprefix, fmt(fn, 'a'), "("); | 
 | 		params(paramlist); | 
 | 		print(")"); | 
 | 		if pc != fn then | 
 | 			print("+", itoa(pc-fn, "%#ux")); | 
 | 		print(" "); | 
 | 		pfl(pc); | 
 | 		if dolocals then | 
 | 			locals(locallist); | 
 | 		if fn == var("threadmain") || fn == var("p9main") then | 
 | 			done=1; | 
 | 		if fn == var("threadstart") || fn == var("scheduler") then | 
 | 			done=1; | 
 | 		if callerpc == 0 then | 
 | 			done=1; | 
 | 	} | 
 | 	if callerpc && !done then { | 
 | 		print(stkprefix, fmt(callerpc, 'a'), " "); | 
 | 		pfl(callerpc); | 
 | 	} | 
 | } | 
 |  | 
 | defn findsrc(file) | 
 | { | 
 | 	local lst, src; | 
 |  | 
 | 	if file[0] == '/' then { | 
 | 		src = file(file); | 
 | 		if src != {} then { | 
 | 			srcfiles = append srcfiles, file; | 
 | 			srctext = append srctext, src; | 
 | 			return src; | 
 | 		} | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	lst = srcpath; | 
 | 	while head lst do { | 
 | 		src = file(head lst+file); | 
 | 		if src != {} then { | 
 | 			srcfiles = append srcfiles, file; | 
 | 			srctext = append srctext, src; | 
 | 			return src; | 
 | 		} | 
 | 		lst = tail lst; | 
 | 	} | 
 | } | 
 |  | 
 | defn line(addr) | 
 | { | 
 | 	local src, file; | 
 |  | 
 | 	file = pcfile(addr); | 
 | 	src = match(file, srcfiles); | 
 |  | 
 | 	if src >= 0 then | 
 | 		src = srctext[src]; | 
 | 	else | 
 | 		src = findsrc(file); | 
 |  | 
 | 	if src == {} then { | 
 | 		print("no source for ", file, "\n"); | 
 | 		return {}; | 
 | 	} | 
 | 	line = pcline(addr)-1; | 
 | 	print(file, ":", src[line], "\n"); | 
 | } | 
 |  | 
 | defn addsrcdir(dir) | 
 | { | 
 | 	dir = dir+"/"; | 
 |  | 
 | 	if match(dir, srcpath) >= 0 then { | 
 | 		print("already in srcpath\n"); | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	srcpath = {dir}+srcpath; | 
 | } | 
 |  | 
 | defn source() | 
 | { | 
 | 	local l; | 
 |  | 
 | 	l = srcpath; | 
 | 	while l do { | 
 | 		print(head l, "\n"); | 
 | 		l = tail l; | 
 | 	} | 
 | 	l = srcfiles; | 
 |  | 
 | 	while l do { | 
 | 		print("\t", head l, "\n"); | 
 | 		l = tail l; | 
 | 	} | 
 | } | 
 |  | 
 | defn Bsrc(addr) | 
 | { | 
 | 	local lst; | 
 |  | 
 | 	lst = srcpath; | 
 | 	file = pcfile(addr); | 
 | 	if file[0] == '/' && access(file) then { | 
 | 		rc("B "+file+":"+itoa(pcline(addr))); | 
 | 		return {}; | 
 | 	} | 
 | 	while head lst do { | 
 | 		name = head lst+file; | 
 | 		if access(name) then { | 
 | 			rc("B "+name+":"+itoa(pcline(addr))); | 
 | 			return {}; | 
 | 		} | 
 | 		lst = tail lst; | 
 | 	} | 
 | 	print("no source for ", file, "\n"); | 
 | } | 
 |  | 
 | defn srcline(addr) | 
 | { | 
 | 	local text, cline, line, file, src; | 
 | 	file = pcfile(addr); | 
 | 	src = match(file,srcfiles); | 
 | 	if (src>=0) then | 
 | 		src = srctext[src]; | 
 | 	else | 
 | 		src = findsrc(file); | 
 | 	if (src=={}) then | 
 | 	{ | 
 | 		return "(no source)"; | 
 | 	} | 
 | 	return src[pcline(addr)-1]; | 
 | } | 
 |  | 
 | defn src(addr) | 
 | { | 
 | 	local src, file, line, cline, text; | 
 |  | 
 | 	file = pcfile(addr); | 
 | 	src = match(file, srcfiles); | 
 |  | 
 | 	if src >= 0 then | 
 | 		src = srctext[src]; | 
 | 	else | 
 | 		src = findsrc(file); | 
 |  | 
 | 	if src == {} then { | 
 | 		print("no source for ", file, "\n"); | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	cline = pcline(addr)-1; | 
 | 	print(file, ":", cline+1, "\n"); | 
 | 	line = cline-5; | 
 | 	loop 0,10 do { | 
 | 		if line >= 0 then { | 
 | 			if line == cline then | 
 | 				print(">"); | 
 | 			else | 
 | 				print(" "); | 
 | 			text = src[line]; | 
 | 			if text == {} then | 
 | 				return {}; | 
 | 			print(line+1, "\t", text, "\n"); | 
 | 		} | 
 | 		line = line+1; | 
 | 	}	 | 
 | } | 
 |  | 
 | defn step()					// single step the process | 
 | { | 
 | 	local lst, lpl, addr, bput; | 
 |  | 
 | 	bput = 0; | 
 | 	if match(*PC, bplist) >= 0 then {	// Sitting on a breakpoint | 
 | 		bput = fmt(*PC, bpfmt); | 
 | 		*bput = @bput; | 
 | 	} | 
 |  | 
 | 	lst = follow(*PC); | 
 |  | 
 | 	lpl = lst; | 
 | 	while lpl do {				// place break points | 
 | 		*(head lpl) = bpinst; | 
 | 		lpl = tail lpl; | 
 | 	} | 
 |  | 
 | 	startstop(pid);				// do the step | 
 |  | 
 | 	while lst do {				// remove the breakpoints | 
 | 		addr = fmt(head lst, bpfmt); | 
 | 		*addr = @addr; | 
 | 		lst = tail lst; | 
 | 	} | 
 | 	if bput != 0 then | 
 | 		*bput = bpinst; | 
 | } | 
 |  | 
 | defn bpset(addr)				// set a breakpoint | 
 | { | 
 | 	if status(pid) != "Stopped" then { | 
 | 		print("Waiting...\n"); | 
 | 		stop(pid); | 
 | 	} | 
 | 	if match(addr, bplist) >= 0 then | 
 | 		print("breakpoint already set at ", fmt(addr, 'a'), "\n"); | 
 | 	else { | 
 | 		*fmt(addr, bpfmt) = bpinst; | 
 | 		bplist = append bplist, addr; | 
 | 	} | 
 | } | 
 |  | 
 | defn bptab()					// print a table of breakpoints | 
 | { | 
 | 	local lst, addr; | 
 |  | 
 | 	lst = bplist; | 
 | 	while lst do { | 
 | 		addr = head lst; | 
 | 		print("\t", fmt(addr, 'X'), " ", fmt(addr, 'a'), "  ", fmt(addr, 'i'), "\n"); | 
 | 		lst = tail lst; | 
 | 	} | 
 | } | 
 |  | 
 | defn bpdel(addr)				// delete a breakpoint | 
 | { | 
 | 	local n, pc, nbplist; | 
 |  | 
 | 	if addr == 0 then { | 
 | 		while bplist do { | 
 | 			pc = head bplist; | 
 | 			pc = fmt(pc, bpfmt); | 
 | 			*pc = @pc; | 
 | 			bplist = tail bplist; | 
 | 		} | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	n = match(addr, bplist); | 
 | 	if n < 0  then { | 
 | 		print("no breakpoint at ", fmt(addr, 'a'), "\n"); | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	addr = fmt(addr, bpfmt); | 
 | 	*addr = @addr; | 
 |  | 
 | 	nbplist = {};				// delete from list | 
 | 	while bplist do { | 
 | 		pc = head bplist; | 
 | 		if pc != addr then | 
 | 			nbplist = append nbplist, pc; | 
 | 		bplist = tail bplist; | 
 | 	} | 
 | 	bplist = nbplist;			// delete from memory | 
 | } | 
 |  | 
 | defn cont()					// continue execution | 
 | { | 
 | 	local addr; | 
 |  | 
 | 	addr = fmt(*PC, bpfmt); | 
 | 	if match(addr, bplist) >= 0 then {	// Sitting on a breakpoint | 
 | 		*addr = @addr; | 
 | 		step();				// Step over | 
 | 		*addr = bpinst; | 
 | 	} | 
 | 	startstop(pid);				// Run | 
 | } | 
 |  | 
 | defn stopped(pid)		// called from acid when a process changes state | 
 | { | 
 | 	pfixstop(pid); | 
 | 	pstop(pid);		// stub so this is easy to replace | 
 | } | 
 |  | 
 | defn procs()			// print status of processes | 
 | { | 
 | 	local c, lst, cpid; | 
 |  | 
 | 	cpid = pid; | 
 | 	lst = proclist; | 
 | 	while lst do { | 
 | 		np = head lst; | 
 | 		setproc(np); | 
 | 		if np == cpid then | 
 | 			c = '>'; | 
 | 		else | 
 | 			c = ' '; | 
 | 		print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n"); | 
 | 		lst = tail lst; | 
 | 	} | 
 | 	pid = cpid; | 
 | 	if pid != 0 then | 
 | 		setproc(pid); | 
 | } | 
 |  | 
 | _asmlines = 30; | 
 |  | 
 | defn asm(addr) | 
 | { | 
 | 	local bound; | 
 |  | 
 | 	bound = fnbound(addr); | 
 |  | 
 | 	addr = fmt(addr, 'i'); | 
 | 	loop 1,_asmlines do { | 
 | 		print(fmt(addr, 'a'), " ", fmt(addr, 'X')); | 
 | 		print("\t", @addr++, "\n"); | 
 | 		if bound != {} && addr > bound[1] then { | 
 | 			lasmaddr = addr; | 
 | 			return {}; | 
 | 		} | 
 | 	} | 
 | 	lasmaddr = addr; | 
 | } | 
 |  | 
 | defn casm() | 
 | { | 
 | 	asm(lasmaddr); | 
 | } | 
 |  | 
 | defn xasm(addr) | 
 | { | 
 | 	local bound; | 
 |  | 
 | 	bound = fnbound(addr); | 
 |  | 
 | 	addr = fmt(addr, 'i'); | 
 | 	loop 1,_asmlines do { | 
 | 		print(fmt(addr, 'a'), " ", fmt(addr, 'X')); | 
 | 		print("\t", *addr++, "\n"); | 
 | 		if bound != {} && addr > bound[1] then { | 
 | 			lasmaddr = addr; | 
 | 			return {}; | 
 | 		} | 
 | 	} | 
 | 	lasmaddr = addr; | 
 | } | 
 |  | 
 | defn xcasm() | 
 | { | 
 | 	xasm(lasmaddr); | 
 | } | 
 |  | 
 | defn win() | 
 | { | 
 | 	local npid, estr; | 
 |  | 
 | 	bplist = {}; | 
 | 	notes = {}; | 
 |  | 
 | 	estr = "/sys/lib/acid/window '0 0 600 400' "+textfile; | 
 | 	if progargs != "" then | 
 | 		estr = estr+" "+progargs; | 
 |  | 
 | 	npid = rc(estr); | 
 | 	npid = atoi(npid); | 
 | 	if npid == 0 then | 
 | 		error("win failed to create process"); | 
 |  | 
 | 	setproc(npid); | 
 | 	stopped(npid); | 
 | } | 
 |  | 
 | defn win2() | 
 | { | 
 | 	local npid, estr; | 
 |  | 
 | 	bplist = {}; | 
 | 	notes = {}; | 
 |  | 
 | 	estr = "/sys/lib/acid/transcript '0 0 600 400' '100 100 700 500' "+textfile; | 
 | 	if progargs != "" then | 
 | 		estr = estr+" "+progargs; | 
 |  | 
 | 	npid = rc(estr); | 
 | 	npid = atoi(npid); | 
 | 	if npid == 0 then | 
 | 		error("win failed to create process"); | 
 |  | 
 | 	setproc(npid); | 
 | 	stopped(npid); | 
 | } | 
 |  | 
 | printstopped = 1; | 
 | defn new() | 
 | { | 
 | 	local a; | 
 | 	 | 
 | 	bplist = {}; | 
 | 	newproc(progargs); | 
 | 	a = var("p9main"); | 
 | 	if a == {} then | 
 | 		a = var("main"); | 
 | 	if a == {} then | 
 | 		return {}; | 
 | 	bpset(a); | 
 | 	while *PC != a do | 
 | 		cont(); | 
 | 	bpdel(a); | 
 | } | 
 |  | 
 | defn stmnt()			// step one statement | 
 | { | 
 | 	local line; | 
 |  | 
 | 	line = pcline(*PC); | 
 | 	while 1 do { | 
 | 		step(); | 
 | 		if line != pcline(*PC) then { | 
 | 			src(*PC); | 
 | 			return {}; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | defn func()			// step until we leave the current function | 
 | { | 
 | 	local bound, end, start, pc; | 
 |  | 
 | 	bound = fnbound(*PC); | 
 | 	if bound == {} then { | 
 | 		print("cannot locate text symbol\n"); | 
 | 		return {}; | 
 | 	} | 
 |  | 
 | 	pc = *PC; | 
 | 	start = bound[0]; | 
 | 	end = bound[1]; | 
 | 	while pc >= start && pc < end do { | 
 | 		step(); | 
 | 		pc = *PC; | 
 | 	} | 
 | } | 
 |  | 
 | defn next() | 
 | { | 
 | 	local sp, bound, pc; | 
 |  | 
 | 	sp = *SP; | 
 | 	bound = fnbound(*PC); | 
 | 	if bound == {} then { | 
 | 		print("cannot locate text symbol\n"); | 
 | 		return {}; | 
 | 	} | 
 | 	stmnt(); | 
 | 	pc = *PC; | 
 | 	if pc >= bound[0] && pc < bound[1] then | 
 | 		return {}; | 
 |  | 
 | 	while (pc < bound[0] || pc > bound[1]) && sp >= *SP do { | 
 | 		step(); | 
 | 		pc = *PC; | 
 | 	} | 
 | 	src(*PC); | 
 | } | 
 |  | 
 | defn maps() | 
 | { | 
 | 	local m, mm; | 
 |  | 
 | 	m = map(); | 
 | 	while m != {} do { | 
 | 		mm = head m; | 
 | 		m = tail m; | 
 | 		print(mm[2]\X, " ", mm[3]\X, " ", mm[4]\X, " ", mm[0], " ", mm[1], "\n"); | 
 | 	} | 
 | } | 
 |  | 
 | defn dump(addr, n, fmt) | 
 | { | 
 | 	loop 0, n do { | 
 | 		print(fmt(addr, 'X'), ": "); | 
 | 		addr = mem(addr, fmt); | 
 | 	} | 
 | } | 
 |  | 
 | defn mem(addr, fmt) | 
 | { | 
 |  | 
 | 	local i, c, n; | 
 |  | 
 | 	i = 0; | 
 | 	while fmt[i] != 0 do { | 
 | 		c = fmt[i]; | 
 | 		n = 0; | 
 | 		while '0' <= fmt[i] && fmt[i] <= '9' do { | 
 | 			n = 10*n + fmt[i]-'0'; | 
 | 			i = i+1; | 
 | 		} | 
 | 		if n <= 0 then n = 1; | 
 | 		addr = fmt(addr, fmt[i]); | 
 | 		while n > 0 do { | 
 | 			print(*addr++, " "); | 
 | 			n = n-1; | 
 | 		} | 
 | 		i = i+1; | 
 | 	} | 
 | 	print("\n"); | 
 | 	return addr; | 
 | } | 
 |  | 
 | defn symbols(pattern) | 
 | { | 
 | 	local l, s, name; | 
 | 	 | 
 | 	l = symbols; | 
 | 	while l do { | 
 | 		s = head l; | 
 | 		if regexp(pattern, s[0]) then { | 
 | 			name = s[4]; | 
 | 			if name == {} then | 
 | 				name = ""; | 
 | 			print(s[0], "\t", s[1], "\t", s[2], "\t", s[3], "\t", name, "\n"); | 
 | 		} | 
 | 		l = tail l; | 
 | 	} | 
 | } | 
 |  | 
 | defn havesymbol(name) | 
 | { | 
 | 	local l, s; | 
 |  | 
 | 	l = symbols; | 
 | 	while l do { | 
 | 		s = head l; | 
 | 		l = tail l; | 
 | 		if s[0] == name then | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | defn spsrch(len) | 
 | { | 
 | 	local addr, a, s, e; | 
 |  | 
 | 	addr = *SP; | 
 | 	s = origin & 0x7fffffff; | 
 | 	e = etext & 0x7fffffff; | 
 | 	loop 1, len do { | 
 | 		a = *addr++; | 
 | 		c = a & 0x7fffffff; | 
 | 		if c > s && c < e then { | 
 | 			print("src(", a, ")\n"); | 
 | 			pfl(a); | 
 | 		}			 | 
 | 	} | 
 | } | 
 |  | 
 | defn acidtypes() | 
 | { | 
 | 	local syms; | 
 | 	local l; | 
 |  | 
 | 	l = textfile(); | 
 | 	if l != {} then { | 
 | 		syms = "acidtypes"; | 
 | 		while l != {} do { | 
 | 			syms = syms + " " + ((head l)[0]); | 
 | 			l = tail l; | 
 | 		} | 
 | 		includepipe(syms); | 
 | 	} | 
 | } | 
 |  | 
 | defn getregs() | 
 | { | 
 | 	local regs, l; | 
 |  | 
 | 	regs = {}; | 
 | 	l = registers; | 
 | 	while l != {} do { | 
 | 		regs = append regs, var(l[0]); | 
 | 		l = tail l; | 
 | 	} | 
 | 	return regs; | 
 | } | 
 |  | 
 | defn setregs(regs) | 
 | { | 
 | 	local l; | 
 |  | 
 | 	l = registers; | 
 | 	while l != {} do { | 
 | 		var(l[0]) = regs[0]; | 
 | 		l = tail l; | 
 | 		regs = tail regs; | 
 | 	} | 
 | 	return regs; | 
 | } | 
 |  | 
 | defn resetregs() | 
 | { | 
 | 	local l; | 
 |  | 
 | 	l = registers; | 
 | 	while l != {} do { | 
 | 		var(l[0]) = register(l[0]); | 
 | 		l = tail l; | 
 | 	} | 
 | } | 
 |  | 
 | defn clearregs() | 
 | { | 
 | 	local l; | 
 |  | 
 | 	l = registers; | 
 | 	while l != {} do { | 
 | 		var(l[0]) = refconst(~0); | 
 | 		l = tail l; | 
 | 	} | 
 | } | 
 |  | 
 | progargs=""; | 
 | print(acidfile); | 
 |  |