/* Fork.c - includes the fork_task used to split up processes... */

#include <linux16/types.h>
#include <linux16/sched.h>
#include <arch/system.h>

/* The idea behind a fork_task is that it seems the best way to mess with 
 * the task register structures is from another task.  In this case, 
 * fork_task is designed to copy FORK_FROM to FORK_TO.  Someday, we can do
 * this as one task since tasks will eventually be inside or outside the
 * kernel.  Now, we only have in-kernel tasks until 0.01 or so, so this is
 * the way it is :)
 * 
 * I also think a kfork_task is/will be a good way to seperate a lot of the
 * actual processor fork stuff from the generic stuff... most/all of the arch
 * stuff will be in kfork_task with a smaller function called by (kernel_)fork. 
 *
 * (Yes, I tried this outside of a task, but it never really worked too well,
 * and to make it work it would have to be one heck of a hack that nobody
 * else (maybe not even me) could *ever* understand :)
 *
 */

__ptask FORK_FROM, FORK_TO;
__u16 ip, newss, oldss, sp, num;

void kfork_task() 
{

/* The loo label is a workaround because bcc can't do long while loops :( */ 
#asm
loo:
#endasm
	num = FORK_TO->t_num;
	save_regs(NEXT);
	NEXT->t_regs.ip = ip;
	*FORK_TO = *FORK_FROM;
	FORK_TO->t_num = num;
	arch_segs.lowss -= 0x400;
	oldss = FORK_FROM->t_regs.ss;
	newss = FORK_TO->t_regs.ss = arch_segs.lowss;
	sp = FORK_TO->t_regs.sp;
#asm
	push ds
	push es
	mov ax, _oldss 
	mov dx, _sp
	mov cx, _newss 
	mov ds, ax
	mov es, cx
	mov bx, 0xfffe
cpy:	mov ax, [bx]
	eseg 
	mov [bx], ax
	dec bx
	dec bx
	cmp bx, dx
	jae cpy	 
	pop es
	pop ds
#endasm	
	FORK_FROM->t_regs.ax = FORK_TO->t_num; 
	FORK_TO->t_regs.ax = 0;
	CURRENT = FORK_TASK;
	NEXT = SCHED_TASK;	
	schedule();
/* Hack for the end of the loop... */
#asm
	push #loo
	ret
#endasm
}

/* For the public function (kernel_fork), we setup a lot of stuff and then
 * get kfork_task scheduled.  We have special exit code for this function
 * which calls schedule() to jump into kfork_task, which then prepares the
 * newly forked task for use.
 * This will probably be put in kernel/sched.c soon.
 */  

void kernel_fork()
{
	__uint tnum;
	__u16 ip, ss;

	/* We create the new task - the task actually does this... */
	tnum=alloc_task();
#asm
	mov ax, [bp+2]
	mov _ip, ax
#endasm
	/* Finally, let's get the tasks going by jumping into fork_task */ 
	FORK_FROM = CURRENT;
	FORK_TO = &task[tnum];
	FORK_TO->t_num = tnum;
	NEXT = FORK_TASK;
	load_regs(NEXT);
	schedule();
	/* If we get here, there's a bug :) */
	while (1) {};  /* Make this a panic once we have printk() */
}
