// idbg - IJVM debugger.
// Copyright 2000 Tom Rothamel <tom-idbg@onegeek.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  

#include <stdio.h>
#include <malloc.h>
#include "idbg.h"

void print_stack(char *what, unsigned int spp) {
	unsigned int spb;
	unsigned int lv = vm->regs->lv;
	unsigned int lvv = vm->m->getword(lv);
	
	Variable *v;
	Method *m;	
		
	spb = vm->m->getword(spp);
	oprint("%s %08X: %+ 8d %c %08X ", what, spp, spb,
	       (spb >= 32 && spb <= 127) ? spb : ' ', spb);

	if (lv <= spp) {
		v = NULL;

		m = lookup_method_addr(vm->regs->pc);
		if (m) v = lookup_variable_num(m->vars, spp - lv);
		if (v) {
			oprint("%s ", v->name);
		} else {
			if (lvv > spp) {
				oprint("var%d ", spp - lv);
			}
		}
	}

	if (spp == vm->regs->sp) oprint("(SP) ");
	if (spp == lv) oprint("(LV) ");
	if (spp == lvv) oprint("(Old PC) ");
	if (spp == lvv + 1) oprint("(Old LV) ");

	oprintf("\n");
}
	
void stack(int len) {
	unsigned int spp;
	unsigned int lv;
	int spb;
	int nlen;
	
	spp = vm->regs->sp;
	lv = vm->regs->lv;

	if (len <= 0) {
		if (spp > lv) {
			nlen = spp - lv + 1;
		} else {
			nlen = spp - 0x7FFF + 1;
		}

		if (len == 0) {
			if (nlen > 10) nlen = 10;
			if (nlen <= 0) nlen = 1;
		}

		len = nlen;
	}
		

	for (; len > 0; spp--,len--) {
		print_stack(" Stack", spp);
	}

}

void do_vars() {
	int lv = vm->regs->lv;
	int nv = 0;
	int vn = 0;
	
	Method *m;
	Variable *v;
	
	m = lookup_method_addr(vm->regs->pc);
	if (!m) return;

	while (v = lookup_variable_num(m->vars, nv)) nv++;

	nv--;
	
	for(; nv >= 0; nv--) {
		print_stack("  Vars", lv + nv);
	}
}
	
int method_name(int pc, int always) {
	Method *m;

	m = lookup_method_addr(pc);
	if (!m && always) oprintf(" <0x06x>", pc);
	if (!m) return 1;

	oprint(" <%s+0x%04x>", m->name, pc - m->addr);
	return 1;
}
	
#define comma {if (do_comma) printf(", "); do_comma = 1;}

int disasm_once(unsigned int pc) {
	Method *m;
	Variable *v;
	Variable *vars = NULL;
	Opcode *op;
	Parse *p;
	int i;

	int do_comma = 0;

	if (lookup_breakpoint(pc)) {
		oprint("B ");
	} else {
		oprint("  ");
	}
	
	if (vm->regs->pc == pc) {
		oprint("PC-> ");
	} else {
		oprint("     ");
	}
	
	oprint("%06X", pc);

	m = lookup_method_addr(pc);
	if (m) {
		vars = m->vars;
	}

	method_name(pc, 0);
	
	oprint(":\t%02X ", vm->m->getbyte(pc));
	
	if (!vm->decode(pc)) {
		oprint("<unknown>\n");
		return 0;
	}

	p = vm->p;
	op = vm->ops[p->opcode];

	for (i = 1; i < 5; i++) {
		if (i < p->len) {
			oprint("%02X ", vm->m->getbyte(pc + i));
		} else {
			oprint("   ");
		}
	}

	if (p->wide) {
		oprint("wide ");
	}

	oprint("%s ", op->name);

	if (op->label) {
		comma;

		if (p->label >= 0) {
			oprint("PC+%04X", p->label);
		} else {
			oprint("PC-%04X", 0 - p->label);
		}

		method_name(pc + p->label, 1);
	}

	
	if (op->variable) {
		comma;
		
		v = lookup_variable_num(vars, p->variable);
		if (v) {
			oprint("%s", v->name);
		} else {
			oprint("var%d", p->variable);
		}
	}

	if (op->constant) {
		comma;

		v = lookup_constant(p->constant);
		if (v) {
			oprint("%s", v->name);
		} else {
			oprint("const%d", p->constant);
		}
	}
		       
	if (op->method) {
		comma;

		v = lookup_constant(p->method);
		if (v) {
			oprint("%s", v->name);
		} else {
			oprint("method%d", p->method);
		}
	}

	if (op->byte) {
		comma;

		oprint("0x%02X", p->byte & 0xFF);
	}
			
	
	oprintf("\n");
	return p->len;
}

int getshort(int ad) {
	return (vm->m->getbyte(ad) << 8) + vm->m->getbyte(ad + 1);
}

void disasm(int pc, int len) {
	Method *m;
	int endpc;
	int ad;
	int pco;

	char *olderr;
	
	if (pc < 0) {
		oprintf("Invalid address.\n");
		return;
	}

	if (len < 0) {
		oprintf("Invalid length.\n");
		return;
	}
	
	if (len == 0) {
		m = lookup_method_addr(pc);
		if (m) {
			pc = m->addr;
			len = m->len - 1;
		} else {
		        len = 0;
		}
	} else {
		len -= 1;
	}


	olderr = vm->err;
	vm->err = NULL;
	
	endpc = pc + len;

	while (pc <= endpc) {
		m = lookup_method_addr(pc);

		if (m) {
			if (pc - m->addr < 4 && m->addr != 0) {
				ad = m->addr;

				oprint("       %06X", ad);
				method_name(ad, 0);
				oprintf(": %d parameters.\n", getshort(ad));
				
				ad += 2;
				
				oprint("       %06X", ad);
				method_name(ad, 0);
				oprintf(": %d local variables.\n", getshort(ad));
		       
				pc = ad + 2;
				continue;
			}
		}

		pco = disasm_once(pc);
		if (!pco) break;
		pc += pco;
	}
	
	if (vm->err) free(vm->err);
	vm->err = olderr;
}

void quick_prompt() {
	oprintf("--------------------------------------------------------------------\n");
	if (vm->err) oprintf("       %s\n", vm->err);
	stack(0);
	if (vm->regs->lv > vm->regs->sp) do_vars();
}

void full_prompt() {
	oprintf("--------------------------------------------------------------------\n");
	if (vm->err) oprintf("       %s\n", vm->err);
	stack(-1);
	if (vm->regs->lv > vm->regs->sp) do_vars();
}

void do_trace() {
	int oldpage = opage;

	opage = -1;

	oprintf("Beginning Trace...\n");
	
	full_prompt();

	while (execute(1)) {
		full_prompt();
		disasm_once(vm->regs->pc);
	}

	opage = oldpage;
}
