Bool CheckPtr(U8 *ptr)
{//Check if addr is valid ptr.
	if (mem_heap_base <= ptr <= mem_mapped_space)
		return *MemPageTable(ptr) & 1;
	else
		return mem_boot_base < ptr < VGAM_GRAPHICS;
}

Bool CheckCodePtr(U8 *ptr)
{//Check if addr is valid code addr.
	if (mem_heap_base <= ptr <= mem_heap_limit)
		return *MemPageTable(ptr) & 1;
	else
		return mem_boot_base < ptr < VGAM_GRAPHICS;
}

Bool CheckOnStack(U8 *ptr, CTask *task=NULL)
{//Check if addr is valid stack addr.
	Bool res = FALSE;

	PUSHFD
	CLI
	if (task)
	{
		if (&task->stack->stack_base <= ptr <= (&task->stack->stack_base)(U8 *) + task->stack->stack_size)
			res = TRUE;
	}
	else if (mem_heap_base <= ptr <= mem_heap_limit)
		res = TRUE;
	POPFD

	return res;
}

I64 UnusedStack(CTask *task=NULL)
{//Count of usused bytes in task's stack.
	I64 res;

	if (!task)
		task = Fs;

	PUSHFD
	CLI
	if (task == Fs)
		res = RSPGet()(U8 *)  - (&task->stack->stack_base)(U8 *);
	else
		res = task->rsp(U8 *) - (&task->stack->stack_base)(U8 *);
	POPFD

	return res;
}

U8 *Caller(I64 num=1)
{//Returns the addr of the fun which called this one,
//or the caller of the caller, etc.
	U8 **rbp = RBPGet, **ptr;

	while (num--)
	{
		if (rbp >= *rbp)
			return NULL;
		rbp = *rbp;
		if (!CheckOnStack(rbp, Fs))
			return NULL;
	}
	ptr = rbp + 1;
	return *ptr;
}

U8 *TaskCaller(CTask *task=NULL, I64 num=0, Bool saved_context=FALSE)
{//Fetches addr of Nth caller on task's stack.
	U8 **ptr, **rbp, **rsp;

	if (!task)
		task = Fs;
	if (!saved_context && task == Fs)
		return Caller(num + 1);
	if (!TaskValidate(task))
		return NULL;
	rbp = task->rbp;
	rsp = task->rsp;
	if (num)
	{
		while (CheckOnStack(rbp, task))
		{
			ptr = rbp + 1;
			if (! --num)
				return *ptr;
			if (rbp >= *rbp)
				break;
			rbp = *rbp;
		}
		return NULL;
	}
	else
	{
		if (task->rip == _RET)
			return *rsp;
		else
			return task->rip;
	}
}
#define STACK_REP_LEN 	32

U0 StackRep(CTask *task=NULL)
{//Reports whats on the stack.
	I64			 i, j, addr, **rbp, **rsp, *my_rsp[STACK_REP_LEN];
	CHashTable	*old_hash = Fs->hash_table;

	if (!task)
		task = Fs;
	if (!TaskValidate(task))
		return;

	PUSHFD
	CLI
	if (task == Fs)
	{
		rbp = RBPGet;
		rsp = rbp + 3;
		rbp = *rbp;
	}
	else
	{
		rsp = task->rsp;
		rbp = task->rbp;
	}
	if (task->rip == _RET)
		addr = *rsp;
	else
		addr = task->rip;
	MemCopy(my_rsp, rsp, STACK_REP_LEN * sizeof(U8 *));
	POPFD

	Fs->hash_table = task->hash_table;
	for (i = 0; i < STACK_REP_LEN; i++)
	{
		"%08X [RSP+%04X]: %016X ", rsp + i, i * sizeof(U8 *), my_rsp[i];
		while (TRUE)
		{
			if (!(&task->stack->stack_base <= rbp < (&task->stack->stack_base)(U8 *) + task->stack->stack_size))
				break;
			j = rbp - rsp;
			if (j >= i)
				break;
			addr = my_rsp[j + 1];
			if (rbp >= my_rsp[j])
				break;
			rbp = my_rsp[j];
		}
		if (my_rsp[i] == addr)
			"$$RED$$";
		"%P$$FG$$\n", my_rsp[i];
	}
	'\n';
	Fs->hash_table = old_hash;
}

U0 CallerRep(U8 **rbp=NULL, CTask *task=NULL)
{//Prints a report of calling routines.
	I64 **ptr;

	if (!task)
		task = Fs;
	if (!rbp)
	{
		if (task == Fs)
			rbp = RBPGet;
		else
			rbp = task->rbp;
	}
	"CallerRep:\n";
	while (CheckOnStack(rbp, task))
	{
		ptr = rbp + 1;
		"%08X:%08tX:%P\n", ptr, *ptr, *ptr;
		if (rbp >= *rbp)
			break;
		rbp = *rbp;
	}
}

U0 D(U8 *addr, I64 count=0x80, Bool show_offset=TRUE)
{//Dump mem, showing offsets.
//See $LK,"DocD",A="MN:DocD"$() for a live dump.
	I64 i, j, ch;
	U8 *ptr = addr;

	while (count)
	{
		if (show_offset)
			"%08X ", ptr - addr;
		else
			"%010X ", ptr;
		if (count > 16)
			j = 16;
		else
			j = count;
		for (i = 0; i < j; i++)
			"%02X ", ptr[i];
		for (; i < 16; i++)
			"   ";
		for (i = 0; i < j; i++)
		{
			ch = ptr[i];
			if (ch < CH_SPACE || ch == CH_BACKSPACE)
				ch = '.';
			'' ch;
			if (ch == '$$')
				'' ch;
		}
		'\n';
		count -= j;
		ptr += j;
	}
}

U0 Dm(U8 *addr, I64 count=0x80)
{//Show mem addr, not offsets.
	D(addr, count, FALSE);
}

U0 Da(U8 **addr, I64 count=0x10)
{//Dump mem, showing symbolic addresses.
	while (count-- > 0)
	{
		"%08X:%08X,%P\n", addr, *addr, *addr;
		addr++;
	}
}

U0 RawPrint(I64 mS=100, U8 *format, ...)
{//Print using $LK,"Raw",A="MN:Raw"$ screen output for a length of time.
//$BK,1$Your heap must be good.$BK,0$
	U8  *buf = StrPrintJoin(NULL, format, argc, argv);
	Bool old_raw, old_input_filter;

	PUSHFD
	CLI
	old_raw = Raw(ON);
	old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK);
	"%s", buf;
	Busy(mS << 10);
	POPFD

	LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter);
	Raw(old_raw);
	Free(buf);
}

U0 RawD(I64 mS=100, U8 *addr, I64 count=0x80)
{//Dumps a block of mem using $LK,"Raw",A="MN:Raw"$
//screen output for a fixed length
	//of time.
	Bool old_raw, old_input_filter;

	PUSHFD
	CLI
	old_raw = Raw(ON);
	old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK);
	D(addr, count);
	Busy(mS << 10);
	POPFD

	LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter);
	Raw(old_raw);
}

U0 RawDm(I64 mS=100, U8 *addr, I64 count=0x80)
{//Dumps a block of mem using $LK,"Raw",A="MN:Raw"$
//screen output for a fixed length
	//of time.
	Bool old_raw, old_input_filter;

	PUSHFD
	CLI
	old_raw = Raw(ON);
	old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK);
	Dm(addr, count);
	Busy(mS << 10);
	POPFD

	LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter);
	Raw(old_raw);
}

I64 *TaskRegAddr(CTask *task, I64 reg_num)
{
	switch (reg_num)
	{
		case REG_RAX:	return &task->rax;
		case REG_RCX:	return &task->rcx;
		case REG_RDX:	return &task->rdx;
		case REG_RBX:	return &task->rbx;
		case REG_RSP:	return &task->rsp;
		case REG_RBP:	return &task->rbp;
		case REG_RSI:	return &task->rsi;
		case REG_RDI:	return &task->rdi;
		case 8 :		return &task->r8;
		case 9 :		return &task->r9;
		case 10:		return &task->r10;
		case 11:		return &task->r11;
		case 12:		return &task->r12;
		case 13:		return &task->r13;
		case 14:		return &task->r14;
		case 15:		return &task->r15;
	}
	return NULL;
}

#define RAWDR_COL 			(text.cols - 40)

U0 RawDr(CTask *task=NULL)
{
	I64  i, j, old_col = text.raw_col;
	Bool old_raw = Raw(ON);
	U8   buf[200];

	if (!task)
		task = Fs;

	for (i = 0; i < 16; i++)
	{
		text.raw_col = i * text.cols + RAWDR_COL;
		"º%3Z:%016X\n", i, "ST_U64_REGS", *TaskRegAddr(task, i);
	}

	text.raw_col = i++ * text.cols + RAWDR_COL;
	"ºRIP:%016X\n", task->rip;

	text.raw_col = i++ * text.cols + RAWDR_COL;
	"º%-*tp\n", text.cols - (RAWDR_COL + 1) - 1, Fs->rip;

	text.raw_col = i++ * text.cols + RAWDR_COL;
	'º';
	if (Bt(&sys_run_level, RLf_COMPILER))
	{
		j = Fs->rip;
		Ui(buf, &j,,, TRUE);
		"%s", buf;
	}
	else
		'\n';

	text.raw_col = i * text.cols + RAWDR_COL;
	'È';
	for (j = 0; j < text.cols - RAWDR_COL - 1; j++)
		'Í';

	text.raw_col = old_col;
	Raw(old_raw);
}

U0 Dr(CTask *task=NULL)
{//Dump regs
	I64 i;

	if (!task)
		task = Fs;
	for (i = 0; i < 16; i++)
		"%3Z:%016X\n", i, "ST_U64_REGS", *TaskRegAddr(task, i);
	"RIP:%016X\n", task->rip;
}

U8 *SysGetStr2(I64)
{
	U8 buf[512];

	StrNGet(buf, 512, FALSE);
	return StrNew(buf);
}

CBpt *BptFind(U8 *needle_addr, CTask *haystack_task=NULL, Bool rem=FALSE)
{
	CBpt *res=NULL, *tmpb, *tmpb1, *tmpb2;

	if (!haystack_task)
		haystack_task = Fs;

	PUSHFD
	CLI
	tmpb1 = &haystack_task->bpt_list;
	tmpb  =  haystack_task->bpt_list;
	while (tmpb)
	{
		tmpb2 = tmpb->next;
		if (tmpb->addr == needle_addr)
		{
			res = tmpb;
			if (rem)
				tmpb1->next = tmpb2;
			else
				tmpb1 = &tmpb->next;
		}
		else
			tmpb1 = &tmpb->next;
		tmpb = tmpb2;
	}
	POPFD

	return res;
}

Bool BptS(U8 *addr, CTask *task=NULL, Bool live=TRUE)
{//Set breakpoint.
	CBpt *tmpb;
	Bool res = TRUE;
	if (!task)
		task = Fs;

	PUSHFD
	CLI
	if (!(tmpb = BptFind(addr, task, FALSE)))
	{
		tmpb = CAlloc(sizeof(CBpt), task);
		tmpb->addr	=  addr;
		tmpb->val	= *addr;
		res = FALSE;
		tmpb->next	=  task->bpt_list;

		task->bpt_list = tmpb;
		if (task == Fs && live)
			*addr = OC_BPT;
	}
	POPFD

	return res;
}

Bool BptR(U8 *addr, CTask *task=NULL, Bool live=TRUE, Bool rem=TRUE)
{//Remove breakpoint.
	CBpt *tmpb;
	Bool  res = FALSE;

	if (!task)
		task = Fs;

	PUSHFD
	CLI
	if (tmpb = BptFind(addr, task, rem))
	{
		if (task == Fs && live)
			*tmpb->addr = tmpb->val;
		res = TRUE;
		if (rem)
			Free(tmpb);
	}
	POPFD

	return res;
}

Bool B(U8 *addr, CTask *task=NULL, Bool live=TRUE)
{//Toggle breakpoint.
//Return: TRUE if removed.
	Bool res = FALSE;

	PUSHFD
	CLI
	if (BptFind(addr, task, FALSE))
	{
		BptR(addr, task, live, TRUE);
		res = TRUE;
	}
	else
		BptS(addr, task, live);
	POPFD

	return res;
}

I64 B2(CTask *task=NULL, Bool live=TRUE)
{//Remove all breakpoints.
//Return: count of removed.
	I64 res = 0;
	CBpt *tmpb, *tmpb1;

	if (!task)
		task = Fs;

	PUSHFD
	CLI
	tmpb = task->bpt_list;
	task->bpt_list = NULL;
	while (tmpb)
	{
		tmpb1 = tmpb->next;
		if (task == Fs && live)
			*tmpb->addr = tmpb->val;
		Free(tmpb);
		tmpb = tmpb1;
		res++;
	}
	POPFD

	return res;
}

U0 G(U8 *ip=INVALID_PTR, CTask *task=NULL)
{//Go
	if (!task)
		task = Fs;
	if (ip != INVALID_PTR)
		task->rip = ip;
	if (BptFind(task->rip, task))
		"\nDo one of the following, first:\n"
		">S;\t\t\t//Single step\n"
		">B2;\t\t\t//Clear all break points\n"
		">G2;\t\t\t//Clear all break points and Go\n\n"
		"After resuming, <CTRL-ALT-n> next focus task\n"
		"After resuming, <CTRL-ALT-v> flushes screen VGA cache\n";
	else
	{
		LBtr(&task->task_flags, TASKf_DISABLE_BPTS);
		LBtr(&task->rflags, RFLAGf_TRAP);//No single step
		Suspend(task, FALSE);
		if (task == Fs)
		{
			if (IsDebugMode && task->next_cc != &task->next_cc)
			{
				"Exit Debug\n";
				Btr(&task->last_cc->flags, CCf_PROMPT);
			}
		}
		else
			Exit;
	}
}

U0 G2(U8 *ip=INVALID_PTR, CTask *task=NULL)
{//Remove all breakpoints and Go.
	if (!task)
		task = Fs;
	B2(task);
	if (ext[EXT_WIN_FOCUS])
		CallExtNum(EXT_WIN_FOCUS, debug.focus_task);
	LFBFlush;
	G(ip, task);
}

public U0 S(U8 *ip=INVALID_PTR, CTask *task=NULL) //Single-step.
{//Single step.
	if (!task)
		task = Fs;

	PUSHFD
	CLI
	if (ip != INVALID_PTR)
		task->rip = ip;
	LBts(&task->task_flags, TASKf_DISABLE_BPTS);
	LBts(&task->rflags, RFLAGf_TRAP);
	Suspend(task, FALSE);
	if (task == Fs)
	{
		if (IsDebugMode)
		{
			if (task->next_cc != &task->next_cc)
				Btr(&task->last_cc->flags, CCf_PROMPT);
		}
	}
	else
		Exit;
	POPFD
}

U0 DebugHelp()
{
	"\n"
	"The cmd line is basically the same as normal.	Here are some common\n"
	"debugging commands.\n\n"
	">EdLite(\"FileName\");\t\t//Edit file.\n"
	">D(0x100000);\t\t\t//Dump page tables.\n"
	">Dm(0x100000);\t\t\t//Dump page tables.\n"
	">Dm(Fs,sizeof(CTask));\t\t//Dump current task record.\n"
	">ClassRep(Fs,\"CTask\",1);\t//Dump current task record.\n"
	">ClassRep(Fs,,1);\t\t//(It knows lastclass.)\n"
	">CallerRep;\t\t\t//Stack trace report.\n"
	">Da(_RSP);\t\t\t//Dump stack.\n"
	">Dr;\t\t\t\t//Dump Regs.\n"
	">1+2*3+&Print;\t\t\t//Show calculation res.\n"
	">*(0x70000)(I64 *)=0x123456789;\t//Assign value to 0x70000-0x70007.\n"
	">_RAX=0x1234;\t\t\t//Set RAX to 0x1234.\n"
	">_RIP=&Break;\t\t//Set RIP.\n"
	">I64 i;\t\t\t\t//Declare variable.\n"
	">i=_RCX+_RDX;\t\t\t//Assign to variable.\n"
	">U(&Print+0x8);\t\t\t//Unassemble Print.\n"
	">Uf(\"Print\");\t\t\t//Unassembler function \"Print\".\n"
	">Man(\"Print\");\t\t\t//Edit Src for \"Print\".\n"
	">E(_RIP);\t\t\t//Edit Src Code.\n"
	">Fix;\t\t\t\t//Edit Last Err Src Code.\n"
	">B(&Main+0x20);\t\t\t//Toggle break point.\n"
	">B2;\t\t\t\t//Clear all break points.\n"
	">S;\t\t\t\t//Single step.\n"
	">G;\t\t\t\t//Resume execution.\n"
	">G2;\t\t\t\t//B2;LFBFlush;WinFocus;G;\n"
	">Exit;\t\t\t\t//Exit (kill) task.\n\n"
	"After resuming, <CTRL-ALT-n> next focus task.\n"
	"After resuming, <CTRL-ALT-v> flushes screen VGA cache.\n\n";
}

U0 Debug2()
{
	Bool old_win_inhibit, old_waiting_message, old_single;
	I64  i, old_getstr2;
	U8   buf[200];

	if (debug.panic)
	{
		if (IsRaw)
		{
			i = Fs->rip;
			Ui(buf, &i);
			"%s", buf;
		}
		else
			U(Fs->rip, 1);
	}
	else
		debug.panic = TRUE;
	old_waiting_message = LBtr(&Fs->task_flags, TASKf_AWAITING_MESSAGE);
	old_win_inhibit = Fs->win_inhibit;
	Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
	sys_focus_task = Fs;
	kbd.scan_code = 0;
	old_getstr2 = fp_getstr2;
	fp_getstr2 = &SysGetStr2;
	old_single = SingleUser(OFF);
	while (!mouse_hard.install_attempts)
		Yield;
	SingleUser(old_single);
	UserTaskCont;
	fp_getstr2 = old_getstr2;
	Fs->win_inhibit = old_win_inhibit;
	LBEqual(&Fs->task_flags, TASKf_AWAITING_MESSAGE, old_waiting_message);
}

U0 Fault3(I64 fault_num, I64 fault_err_code)
{
	no_warn fault_err_code;

	PUSHFD
	CLI
	if (Gs->num && debug.mp_crash)
	{
		mp_count = 1;
		debug.mp_crash->cpu_num		= Gs->num;
		debug.mp_crash->task		= Fs;
		debug.mp_crash->rip			= Fs->rip;
		debug.mp_crash->message		= debug.message;
		debug.mp_crash->message_num	= debug.message_num;
		MPInt(I_MP_CRASH, 0);
		SysHlt;
	}

	"\n\tZenithOS Debugger\n\n"
	">Help;\t//For help.\n\n";

	Beep(62, TRUE);
	if (fault_num == I_DEBUG)
	{
		if (debug.message)
		{
			"\n!!! %s", debug.message;
			if (debug.message_num)
				"%016X", debug.message_num;
			" !!!\n\n";
		}
	}
	if (debug.panic)
		CallerRep;
	Debug2;
	POPFD
}

U0 Fault2(I64 fault_num, I64 fault_err_code)
{//Called from $LK,"Fault2",A="FF:::/Kernel/KInterrupts.CC,Fault2"$.
//$BK,1$Be careful not to swap-out and ruin the saved context$BK,0$
	Bool was_raw, was_single_user, was_silent, was_in_debug, was_mouse_enabled;
	I64  i, old_raw_flags = text.raw_flags;

	was_single_user = SingleUser(ON);
	if (!IsDebugMode)
		debug.focus_task = sys_focus_task;
	sys_focus_task = NULL;
	if (fault_num == I_BPT)
		Fs->rip--;
	if (Fs->debug_task)
		CallExtNum(EXT_DEBUG_RESUME, fault_num, fault_err_code);
	else
	{
		was_mouse_enabled = CallExtStr("MouseHardEnable", FALSE);
		was_raw = Raw(ON);
		was_silent = Silent(OFF);
		text.raw_flags |= RAWF_SHOW_DOLLAR|RAWF_SCROLL;

		"Task \"";
		"%s", Fs->task_title;
		"\"\n";
		"Fault:0x%02X %Z\t\tErr Code:%08X\n", fault_num, fault_num, "ST_INT_NAMES", fault_err_code;
		was_in_debug = DebugMode(ON);
		"RIP:%08X", Fs->rip; //Sometimes crashes on %p, so do this first
		":%p  RSP:%08X\n", Fs->rip, Fs->rsp;
		if (fault_num == I_PAGE_FAULT)
		{
			MOV_RAX_CR2
			i = RAXGet;
			"Fault Addr:%08X:%p\n", i, i;
		}
		Fault3(fault_num, fault_err_code);
		DebugMode(was_in_debug);
		Silent(was_silent);
		Raw(was_raw);
		CallExtStr("MouseHardEnable", was_mouse_enabled);
		text.raw_flags = old_raw_flags;
	}
	SingleUser(was_single_user);
	if (LBtr(&Fs->task_flags, TASKf_KILL_AFTER_DEBUG))
		Exit;
}

U0 Panic(U8 *message=NULL, I64 message_num=0, Bool panic=TRUE)
{//Enter the debugger with panic?
	PUSHFD
	CLI
	debug.message		= message;
	debug.message_num	= message_num;
	debug.panic			= panic;
	INT I_DEBUG
	POPFD
}

U0 Debug(U8 *message=NULL, I64 message_num=0)
{//Enter debugger, no panic.
	Panic(message, message_num, FALSE);
}
