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 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 Raw screen output for a length of time.
//Your heap must be good.
        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 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 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\tZealOS 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 Fault2.
//Be careful not to swap-out and ruin the saved context
        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);
}