U0 Exit()
{//Terminate own task.
    if (Fs == sys_focus_task && IsDebugMode)
    {
        LBts(&Fs->task_flags, TASKf_KILL_AFTER_DEBUG);
        G;
    }
    else
    {
        if (!Gs->num && !IsDebugMode)
            SingleUser(OFF);

        Fs->rflags  = RFlagsGet;
        Fs->rsp     = RSPGet;
        Fs->rbp     = RBPGet;
        Fs->rip     = $;
        CLI
        LBts(&Fs->task_flags, TASKf_KILL_TASK);
        TaskEndNow;
    }
}

Bool TaskValidate(CTask *task)
{//return TRUE if task looks valid.
    if (!(0 < task <= I32_MAX) || task->addr != task || task->task_signature != TASK_SIGNATURE_VAL)
        return FALSE;
    else
        return TRUE;
}

I64 BirthWait(CTask **_task, I64 task_num=-1)
{//Wait for task valid and not task_num.
    while (!TaskValidate(*_task) || (*_task)->task_num == task_num)
        Yield;

    return (*_task)->task_num;
}

U0 DeathWait(CTask **_task, Bool send_exit=FALSE)
{//Wait for task death.
    if (send_exit && TaskValidate(*_task))
    {
        TaskWait(*_task, TRUE);
        XTalk(*_task, "Exit;\n");
    }
    while (TaskValidate(*_task))
        Yield;
}

Bool Kill(CTask *task, Bool wait=TRUE, Bool just_break=FALSE)
{//Terminate other task.
    I64 i;

    if (TaskValidate(task))
    {
        if (just_break)
        {
            if (task != Fs)
                Break;
            else
            {//TODO wait
                sys_focus_task = task;
                LBts(sys_ctrl_alt_flags, CTRL_ALT_C);
                return TRUE;
            }
        }
        else
        {
            if (task != sys_winmgr_task)
            {
                for (i = 0; i < mp_count; i++)
                    if (task == cpu_structs[i].executive_task)
                        return FALSE;
                LBts(&task->task_flags, TASKf_KILL_TASK);
                if (wait)
                {
                    do Yield;
                    while (TaskValidate(task) && Bt(&task->task_flags, TASKf_KILL_TASK));
                }
                return TRUE;
            }
        }
    }

    return FALSE;
}

Bool Suspend(CTask *task=NULL, Bool state=TRUE)
{//Tell scheduler to skip task.
    Bool res;

    if (!task)
        task = Fs;

    PUSHFD
    CLI
    if (TaskValidate(task))
        res = LBEqual(&task->task_flags, TASKf_SUSPENDED, state);
    else
        res = FALSE;
    POPFD

    return res;
}

Bool IsSuspended(CTask *task=NULL)
{//You might use this in a DrawIt() or Animatetask().
    if (!task)
        task = Fs;

    if (TaskValidate(task))
        return Bt(&task->task_flags, TASKf_SUSPENDED);
    else
        return FALSE;
}

CTaskStack *TaskStackNew(I64 stack_size, CTask *task)
{
    CTaskStack *tmps = MAlloc(stack_size + offset(CTaskStack.stack_base), task);

    tmps->next_stack    = NULL;
    tmps->stack_ptr     = NULL;
    tmps->stack_size    = MSize(tmps) - offset(CTaskStack.stack_base);

    return tmps;
}

#exe {Option(OPTf_NO_REG_VAR, ON);};
argpop I64 CallStackGrow(I64 stack_size_threshold, I64 stack_size, /*argpop*/I64 (*fp_addr)(...), ...)
{//Grow stack in call with any fixed num of args.
//See ::/Demo/StackGrow.ZC.
    CTaskStack  *tmps, *tmps2, **_stack;
    I64          res, *rsp, *rsp2, *old_stack;

    if (UnusedStack >= stack_size_threshold)
    {

        asm {
            LEAVE
            POP             RAX     //return addr
            ADD             RSP, 16 //pop threshold, stack_size
            POP             RBX     // *f
            ADD             RSP, 8  //pop ARGC
            PUSH            RAX
            JMP             RBX     //CALL fp_addr()
        };

    }
    else
    {

        tmps2 = TaskStackNew(stack_size, Fs);
        tmps2->next_stack = tmps = Fs->stack;
        rsp2 = (&tmps2->stack_base)(U8 *) + tmps2->stack_size;
        old_stack = rsp = &argv[argc];
        while (argc-- > 0)
            *--rsp2 = *--rsp;
        _stack = &Fs->stack;
        tmps->stack_ptr = rsp = RSPGet;
        asm {
            IMPORT          _FREE;  //We are in a function, not at global scope.
//The compiler treats these in isolation.

            PUSHFD
            POP             RDX             //flags
            CLI
            MOV             RBX, U64 &tmps2[RBP]
            MOV             RAX, &_stack[RBP]
            MOV             U64 [RAX], RBX  //Fs->stack=tmps2
            MOV             RSP, U64 &rsp2[RBP]
            PUSH            RDX
            POPFD

            CALL            U64 &fp_addr[RBP]
            MOV             U64 &res[RBP], RAX

            PUSHFD
            POP             RDX             //flags
            CLI
            MOV             RBX, U64 &tmps[RBP]
            MOV             RAX, &_stack[RBP]
            MOV             U64 [RAX], RBX  //Fs->stack=tmps
            MOV             RSP, U64 &rsp[RBP]
            PUSH            RDX
            POPFD

            PUSH            U64 &tmps2[RBP]
            CALL            _FREE

            MOV             RDX, U64 &old_stack[RBP]
            MOV             RBX, U64 8[RBP]
            MOV             RAX, U64 &res[RBP]
            MOV             RBP, U64 [RBP]
            MOV             RSP, RDX
            JMP             RBX             //return
        };
    }

    return 0; //dummy to get rid of warning
}
;
#exe {Option(OPTf_NO_REG_VAR, OFF);};

I64 TaskInit(CTask *task, I64 stack_size)
{//Returns Fs of task
    CTaskStack *tmps;

    QueueInit(&task->code_heap->next_mem_blk);
    task->code_heap->last_mergable = NULL;
    if (task->code_heap != task->data_heap)
    {
        QueueInit(&task->data_heap->next_mem_blk);
        task->data_heap->last_mergable = NULL;
    }

    task->addr = task->next_task = task->last_task = task->next_input_filter_task = task->last_input_filter_task = task;

    task->task_num = sys_num_spawned_tasks++;

    task->rflags = RFLAGG_NORMAL;
    task->win_inhibit = WIG_TASK_DEFAULT;

    task->next_child_task = task->last_child_task = (&task->next_child_task)(U8 *) - offset(CTask.next_sibling_task);

    JobCtrlInit(&task->server_ctrl);
    QueueInit(&task->next_cc);
    QueueInit(&task->next_except);
    QueueInit(&task->next_ctrl);
    QueueInit(&task->next_ode);

    task->fpu_mmx = MAllocAligned(sizeof(CFPU), 0x10, task);
    MemCopy(task->fpu_mmx, SYS_FIXED_AREA + offset(CSysFixedArea.init_fpu_mmx), sizeof(CFPU));

    task->hash_table = HashTableNew(TASK_HASH_TABLE_SIZE, task);

    if (!stack_size)
        stack_size = MEM_DEFAULT_STACK;

    task->stack = tmps = TaskStackNew(stack_size, task);
    task->rsp = (&tmps->stack_base)(U8 *) + tmps->stack_size;

    task->text_attr             = WHITE << 4 + BLUE;
    task->border_src            = BDS_CONST;
    task->border_attr           = DriveTextAttrGet(':');
    task->title_src             = TTS_CONST;
    task->win_left              = 1;
    task->win_right             = text.cols - 2;
    task->win_top               = text.rows / 8 + 3;
    task->win_bottom            = text.rows - 2;

    if (blkdev.home_dir)
    {//Beware System TaskInit. Maybe ok until DiskChange().
        task->cur_dv    = blkdev.let_to_drive[*blkdev.home_dir - 'A'];
        task->cur_dir   = StrNew(blkdev.home_dir + 2, task);
    }
    else
        task->cur_dir   = StrNew("/Home", task);

    Seed(, task);

    return task;
}

CTask *Spawn(U0 (*fp_start_addr)(U8 *data), U8 *data=NULL, U8 *task_name=NULL,
                I64 target_cpu=-1,  // -1 for current CPU. See multi-core.
                CTask *parent=NULL, // NULL means sys_task
                I64 stack_size=0,   // 0=default
                I64 flags=1 << JOBf_ADD_TO_QUE)
{//Create task on core running at address.
//Alloc CTask structure from code heap so address will be short.
    //Could be alloced off of data heap.
    CTask *task;

    if (target_cpu >= 0)
        return SpawnQueue(fp_start_addr, data, task_name, target_cpu, parent, stack_size, flags);

    task = CAlloc(sizeof(CTask), sys_task->code_heap);
    task->task_signature = TASK_SIGNATURE_VAL;
    if (!task_name)
        task_name = "Unnamed Task";
    if (!parent)
        parent = Gs->executive_task;
    task->parent_task = parent;
    task->gs = parent->gs;
    if (sys_code_bp)
        task->code_heap = HeapCtrlInit(, task, sys_code_bp);
    if (sys_data_bp)
        task->data_heap = HeapCtrlInit(, task, sys_data_bp);
    else
        task->data_heap = task->code_heap;
    TaskInit(task, stack_size);
    task->rip = fp_start_addr;
    task->rsp(U8 *) -= 8;
    *task->rsp = data;
    task->rsp(U8 *) -= 8;
    *task->rsp = &Exit;
    task->hash_table->next = parent->hash_table;
    MemCopy(task->task_name, task_name, TASK_NAME_LEN);
    StrCopy(task->task_title, task->task_name);
    task->title_src = TTS_TASK_NAME;

    PUSHFD
    CLI
    if (Bt(&flags, JOBf_ADD_TO_QUE))
    {
        TaskQueueInsChild(task);
        TaskQueueIns(task);
    }
    POPFD

    return task;
}

U0 TaskDerivedValsUpdate(CTask *task=NULL, Bool update_z_buf=TRUE)
{//Those things calculated from other variables.
    if (!task)
        task = Fs;

    PUSHFD
    CLI
    while (LBts(&task->task_flags, TASKf_TASK_LOCK))
        PAUSE
    WinDerivedValsUpdate(task);
    if (fp_update_ctrls)
        (*fp_update_ctrls)(task);
    if (update_z_buf && Bt(&task->display_flags, DISPLAYf_SHOW))
        LBts(&sys_semas[SEMA_UPDATE_WIN_Z_BUF], 0);
    LBtr(&task->task_flags, TASKf_TASK_LOCK);
    POPFD
}

I64 ExeCmdLine(CCompCtrl *cc)
{//Terminal JIT-compile-and-execute loop for CCompCtrl.
    I64          res = 0, type, old_title_src = Fs->title_src;
    U8          *ptr, *ptr2, *ptr3, *machine_code, *old_task_title = StrNew(Fs->task_title);
    F64          t0;
    CDocEntry   *doc_e;
    CDoc        *doc;

    if (Fs->title_src != TTS_LOCKED_CONST)
        Fs->title_src = TTS_CUR_LEX;
    while (cc->token && (cc->token != '}' || !(cc->flags & CCF_EXE_BLK)) )
    {
        if (Fs->title_src == TTS_CUR_LEX)
        {
            ptr2 = &Fs->task_title;
            ptr3 = ptr2 + STR_LEN - 1;
            if (cc->lex_include_stack->flags & LFSF_DOC)
            {
                doc_e = cc->lex_include_stack->cur_entry;
                doc   = cc->lex_include_stack->doc;
                while (doc_e != doc && ptr2 < ptr3)
                {
                    switch (doc_e->type_u8)
                    {
                        case DOCT_TEXT:
                            ptr = doc_e->tag;
                            while (*ptr && ptr2 < ptr3)
                                *ptr2++ = *ptr++;
                            break;

                        case DOCT_TAB:
                        case DOCT_NEW_LINE:
                            *ptr2++ = '.';
                            break;
                    }
                    doc_e = doc_e->next;
                }
                if (ptr2 < ptr3)
                    *ptr2 = 0;
            }
            else
                if ((ptr = cc->lex_include_stack->line_start) && *ptr)
                    MemCopy(ptr2, ptr, STR_LEN - 1);
        }
        cc->flags &= ~CCF_HAS_MISC_DATA;
        machine_code = LexStatement2Bin(cc, &type);
        if (machine_code != INVALID_PTR)
        {
            if (!(cc->flags & CCF_JUST_LOAD))
            {
                t0 = tS;
                res = Call(machine_code);
                Fs->answer      = res;
                Fs->answer_type = type;
                Fs->answer_time = tS - t0;
                Fs->new_answer  = TRUE;
                cc->prompt_line = 0;
            }
            if (!(cc->flags & CCF_HAS_MISC_DATA))
                Free(machine_code);
        }
    }
    if (Fs->title_src != TTS_LOCKED_CONST)
    {
        Fs->title_src = old_title_src;
        StrCopy(Fs->task_title, old_task_title);
    }
    Free(old_task_title);
    if (cc->flags & CCF_JUST_LOAD)
    {
        if (cc->error_count)
            return FALSE;
        else
            return TRUE;
    }
    else
        return res;
}

U0 ServerTaskCont()
{//Act as server task in a loop handling commands.
    I64 old_flags = RFlagsGet;

    FlushMessages;
    while (TRUE)
    {
        CLI
        if (JobsHandler(old_flags) && Fs->title_src == TTS_TASK_NAME)
            MemCopy(Fs->task_title, Fs->task_name, TASK_NAME_LEN);
        FlushMessages;
        LBts(&Fs->task_flags, TASKf_IDLE);
        LBts(&Fs->task_flags, TASKf_AWAITING_MESSAGE);
        Yield;
        RFlagsSet(old_flags);
    }
}

U0 UserTaskCont()
{//Terminal key-input-execute loop.
    CCompCtrl   *cc;
    CDoc        *doc;
    Bool         cont = TRUE;

    do
    {
        cc = CompCtrlNew(, CCF_CMD_LINE | CCF_PROMPT | CCF_QUESTION_HELP);
        QueueInsert(cc, Fs->last_cc);
        try
        {
            Lex(cc);
            ExeCmdLine(cc);
            cont = Bt(&cc->flags, CCf_PROMPT);
            QueueRemove(cc);
            CompCtrlDel(cc);
        }
        catch
        {
            if ((doc = Fs->put_doc) && doc->doc_signature == DOC_SIGNATURE_VAL)
                DocUnlock(doc);
            PutExcept;
        }
    }
    while (cont);
}

U0 ServerCmdLine(I64 dummy=0)
{
    no_warn dummy;
    Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
    CallExtStr("ServerStartUp");
    ServerTaskCont;
}

U0 UserCmdLine(I64 dummy=0)
{//A user task ends-up calling this.
    no_warn dummy;
    Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
    CallExtStr("UserStartUp");
    if (!LBts(&Fs->display_flags, DISPLAYf_SHOW))
        Debug;
    UserTaskCont;
}

CTask *User(U8 *format=NULL, ...)
{//Create user term task.
    U8      *st;
    CTask   *task = Spawn(&UserCmdLine,, "Terminal");

    TaskWait(task);
    if (format)
    {
        st = StrPrintJoin(NULL, format, argc, argv);
        XTalk(task, st);
        Free(st);
    }

    return task;
}

U0 TaskDel(CTask *task)
{//We delay freeing in case lingering pointer to reincarnated.
    HeapCtrlDel(task->code_heap);
    if (task->data_heap != task->code_heap)
        HeapCtrlDel(task->data_heap);
    Free(task);
}

I64 TaskEnd()
{//Called with irqs off.
    CTask *task = Fs, *tmpt, *tmpt1;

    if (task == sys_task_being_screen_updated)
    {
        LBts(&task->task_flags, TASKf_KILL_TASK);
        return task->next_task;
    }
    if (task->task_end_cb)
    {
        task->wake_jiffy = 0;
        LBtr(&task->task_flags, TASKf_KILL_TASK);
        TaskResetAwaitingMessage(task);
        Suspend(task, FALSE);
        task->rip = task->task_end_cb;
        task->task_end_cb = NULL;
        return task;
    }
    if (task->parent_task && task->parent_task->popup_task == task)
    {
        task->parent_task->popup_task = NULL;
        Kill(task->parent_task, FALSE);
        return task->parent_task;
    }

    DrivesRelease;
    BlkDevsRelease;
    tmpt1 = (&task->next_child_task)(U8 *) - offset(CTask.next_sibling_task);
    tmpt = tmpt1->next_sibling_task;
    if (tmpt != tmpt1)
    {
        do
        {
            LBts(&tmpt->task_flags, TASKf_KILL_TASK);
            tmpt = tmpt->next_sibling_task;
        }
        while (tmpt != tmpt1);

        return task->next_task;
    }
    if (LBtr(&task->display_flags, DISPLAYf_SHOW))
        LBts(&sys_semas[SEMA_UPDATE_WIN_Z_BUF], 0);

    while (LBts(&task->task_flags, TASKf_TASK_LOCK))
        PAUSE
    while (LBts(&task->server_ctrl.flags, JOBCf_LOCKED))
        PAUSE

    JobQueueDel(&task->server_ctrl.next_waiting);
    JobQueueDel(&task->server_ctrl.next_done);

    if (IsRaw)
        LFBFlush;

    if (sys_focus_task == task)
    {
        if (!Gs->num)
            SingleUser(OFF);
        sys_focus_task = NULL;
        if (fp_set_std_palette)
            (*fp_set_std_palette)();
    }

    //QueueRemove
    task->task_signature(I64) = 0;

    tmpt  =task->next_input_filter_task;
    tmpt1 =task->last_input_filter_task;
    tmpt1->next_input_filter_task = tmpt;
    tmpt->last_input_filter_task  = tmpt1;

    tmpt  = task->next_sibling_task;
    tmpt1 = task->last_sibling_task;
    tmpt1->next_sibling_task = tmpt;
    tmpt->last_sibling_task  = tmpt1;

    tmpt = task->next_task; //save to return
    TaskQueueRemove(task);

    LBtr(&task->server_ctrl.flags, JOBCf_LOCKED);
    LBtr(&task->task_flags, TASKf_TASK_LOCK);

    task->wake_jiffy = counts.jiffies + DYING_JIFFIES;
    while (LBts(&Gs->cpu_flags, CPUf_DYING_TASK_QUE))
        PAUSE
    QueueInsert(task, Gs->last_dying);
    LBtr(&Gs->cpu_flags, CPUf_DYING_TASK_QUE);

    return tmpt;
}

U0 TaskKillDying()
{//Delay freeing to prevent asking for trouble with quick reincarnations.
//What if the user is doing this: DoTreeCheckers.
    CTaskDying *task, *task1;

    if (Gs->kill_jiffy < counts.jiffies)
    {//Avoid doing as many lock operations.
        while (LBts(&Gs->cpu_flags, CPUf_DYING_TASK_QUE))
            PAUSE
        task = Gs->next_dying;
        while (task != &Gs->next_dying && task->wake_jiffy < counts.jiffies)
        {
            task1 = task->next;
            QueueRemove(task);
            TaskDel(task);
            task = task1;
        }
        LBtr(&Gs->cpu_flags, CPUf_DYING_TASK_QUE);
        Gs->kill_jiffy = counts.jiffies + DYING_JIFFIES;
    }
}