Bool CheckPtr(U8 *ptr)
{//Check if address is valid pointer.
    CZXE *zxe = mem_boot_base - sizeof(CZXE);

    if (mem_heap_base <= ptr <= mem_mapped_space)
        return *MemPageTable(ptr) & 1;
    else
        return mem_boot_base < ptr < mem_boot_base + zxe->file_size - 1 - sizeof(CZXE);
}

Bool CheckCodePtr(U8 *ptr)
{//Check if address is valid code address.
    CZXE *zxe = mem_boot_base - sizeof(CZXE);

    if (mem_heap_base <= ptr <= mem_heap_limit)
        return *MemPageTable(ptr) & 1;
    else
        return mem_boot_base < ptr < mem_boot_base + zxe->file_size - 1 - sizeof(CZXE);
}

Bool CheckOnStack(U8 *ptr, CTask *task=NULL)
{//Check if address is valid stack address.
    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 address of the function 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 address 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;
    if (!IsRaw)
        "$LTCYAN$";
    "RSP       RSP+Offset: Value            Value/Link\n";
    if (!IsRaw)
        "$FG$";
    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 && !IsRaw)
            "$BG,YELLOW$";
        "%P", my_rsp[i];
        if (!IsRaw)
            "$BG$";
        '\n';
    }
    '\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";
    if (!IsRaw)
        "$LTCYAN$RBP:       *RBP:     *RBP:$FG$\n";
    else
        "RBP:       *RBP:     *RBP\n";
    while (CheckOnStack(rbp, task))
    {
        ptr = rbp + 1;
        "%08X: %08tX: %P\n", ptr, *ptr, *ptr;
        if (rbp >= *rbp)
            break;
        rbp = *rbp;
    }
}

U0 Dump(U8 *addr, I64 count=0x80, Bool show_offset=TRUE)
{//Dump mem, showing offsets.
//See DocDump() 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 DumpMem(U8 *addr, I64 count=0x80)
{//Show mem address, not offsets.
    Dump(addr, count, FALSE);
}

U0 DumpAddress(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 RawDump(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);
    Dump(addr, count);
    Busy(mS << 10);
    POPFD

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

U0 RawDumpMem(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);
    DumpMem(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 RawDumpRegs(CTask *task=NULL)
{
    I64  i, j, old_col = text.raw_col;
    Bool old_raw = Raw(ON);
    U8   buf[200];

    if (!task)
        task = Fs;

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

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

    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 = text.cols - 1;
    '.';
    for (j = 2; j < i + 1; j++)
    {
        text.raw_col = j * text.cols - 1;
        '.';
    }
    text.raw_col = j * text.cols - 1;
    '.';

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

U0 DumpRegs(CTask *task=NULL)
{//Dump registers
    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))
        "\nBreakpoints found.\n"
        "Do one of the following, first:\n"
        ">S;\t\t\t//Single step\n"
        ">B2;\t\t//Clear all break points\n"
        ">G2;\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\t\t// Edit file.\n"
    ">Dump(0x100000);\t\t\t\t\t// Dump page tables.\n"
    ">DumpMem(0x100000);\t\t\t\t\t// Dump page tables.\n"
    ">DumpMem(Fs, sizeof(CTask));\t\t// Dump current task record.\n"
    ">ClassRep(Fs, \"CTask\", 1);\t\t\t// Dump current task record.\n"
    ">ClassRep(Fs,, 1);\t\t\t\t\t// (It knows lastclass.)\n"
    ">CallerRep;\t\t\t\t\t\t\t// Stack trace report.\n"
    ">DumpAddress(_RSP);\t\t\t\t\t// Dump stack.\n"
    ">DumpRegs;\t\t\t\t\t\t\t// Dump Registers.\n"
    ">1 + 2 * 3 + &Print;\t\t\t\t// Show calculation res.\n"
    ">*(0x70000)(I64 *) = 0x123456789;\t// Assign value to 0x70000-0x70007.\n"
    ">_RAX = 0x1234;\t\t\t\t\t\t// Set RAX to 0x1234.\n"
    ">_RIP = &Break;\t\t\t\t\t\t// Set RIP.\n"
    ">I64 i;\t\t\t\t\t\t\t\t// Declare variable.\n"
    ">i = _RCX + _RDX;\t\t\t\t\t// Assign to variable.\n"
    ">U(&Print+0x8);\t\t\t\t\t\t// Unassemble Print.\n"
    ">Uf(\"Print\");\t\t\t\t\t\t// Unassembler function \"Print\".\n"
    ">Man(\"Print\");\t\t\t\t\t\t// Edit Src for \"Print\".\n"
    ">E(_RIP);\t\t\t\t\t\t\t// Edit Src Code.\n"
    ">Fix;\t\t\t\t\t\t\t\t// Edit Last Err Src Code.\n"
    ">B(&Main+0x20);\t\t\t\t\t\t// Toggle break point.\n\n"
    ">B2;\t\t\t\t\t\t\t\t// Clear all break points.\n"
    ">S;\t\t\t\t\t\t\t\t\t// Single step.\n"
    ">G;\t\t\t\t\t\t\t\t\t// Resume execution.\n"
    ">G2;\t\t\t\t\t\t\t\t// B2;LFBFlush;WinFocus;G;\n"
    ">Exit;\t\t\t\t\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;
    "\n";
    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"
    "  Type Help; 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\n";
        "Fault:    0x%02X %Z\n", fault_num, fault_num, "ST_INT_NAMES";
        "Err Code: %08X\n", fault_err_code;
        was_in_debug = DebugMode(ON);
        "RIP:      %08X", Fs->rip; //Sometimes crashes on %p, so do this first
        ":%p\nRSP:      %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);
}