#include <linuxmt/types.h>
#include <linuxmt/sched.h>

/* These are various global defs used by the asm code */

/* First we start with some handy assembly code.  I love the way bcc
 * lets us do assembly and C in the same file without limits :)
 */

/* Taskswitch (int 0x80) : perform with interrupts *off*! 

   Basically, we save the registers, figure out what current is, and then
   go ahead and switch to the other task.

   This is now usually going to go to the scheduler...    
*/

__arch_mminit arch_segs;

#asm
_schedjump:
	push bp
	push es
	push ds
	push si
	push di
	push dx
	push cx
	push bx
	push ax
	xor ax, ax
	mov ds, ax
	mov ax, [0x300]
	mov ds, ax
	mov bx, #_CURRENT
	mov di, [bx]
	mov [di+2], sp
	mov [di+4], ss
	mov bx, #_NEXT
	mov si, [bx]
	mov sp, [si+2]
	mov ss, [si+4]
!	Switch things around to make sure it comes back on the next int 0x80
	mov [bx], di
	mov bx, #_CURRENT
	mov [bx], si
	pop ax
	pop bx
	pop cx
	pop dx
	pop di
	pop si 
	pop ds
	pop es
	pop bp
	sti
	iret
#endasm

/* Schedule() calls this right after the task switch to it is completed.
 * 
 * NOTE: The scheduler itself should *never* have this done to it! 
 *
 */

void schedule()
{
#asm
	pop ax
	pushf
	push cs
	push ax
	jmp _schedjump
#endasm
}

void save_regs(task)
__ptask task;
{
#asm
	push bp
	mov bp, sp
	mov si, sp 
	mov di, ss
	mov bx, 4[bp]
	mov sp, [bx+2]
	mov ss, [bx+4]
	add bx, #6
	mov cx, #13
saveloop:	pop ax
	mov [bx], ax
	inc bx
	inc bx
	loop saveloop
	mov ax, sp
	mov sp, si
	mov ss, di	
	mov bx, 4[bp]
	mov [bx+2], ax
	pop bp
#endasm
}

void load_regs(task)
__ptask task;
{
#asm
	mov bp, sp
	mov si, sp
	mov di, ss
	mov bx, 2[bp]
	mov sp, [bx+2]
	mov ss, [bx+4]
	mov cx, #13
	add bx, #30 
loadl:	mov ax, [bx]
	push ax
	dec bx
	dec bx
	loop loadl
	mov ss, di
	mov bx, 2[bp]
	mov [bx+2], sp
	mov sp, si
#endasm
}

/* This sets up registers for an in-kernel task */

void arch_setupregs(regs)
__pregisters regs;
{
	regs->ax = regs->bx = regs->cx = regs->dx = 0;
	regs->cs = arch_segs.cs;
	regs->es = regs->ds = arch_segs.ds;	
	regs->ss = arch_segs.ss;
	regs->sp = regs->bp = regs->si = regs->di = 0;
}

extern void schedule_task();

void arch_setuptasks()
{
/* Hack alert - we have to make task[0].t_num != 0 so it is used... */
	task[0].t_num = 1;
	build_task();	/* Assume 0 is returned here */
	arch_setupregs(&task[0].t_regs);
	task[0].t_regs.ss = arch_segs.lowss;
	task[0].t_regs.flags = 0x208;
	build_task();	/* Assume 1 is returned here */
	arch_setupregs(&task[1].t_regs);
	arch_segs.lowss -= 0x400;
	task[1].t_regs.ss = arch_segs.lowss;
	task[1].t_regs.cs = 0x1002;
	task[1].t_regs.flags = 0x208;
	task[1].t_regs.ip = (__u16)schedule_task; 
	load_regs(&task[1]);
}

void * memcpy();
void kernel_fork2();
__u16 ss1, ss2, sp;

/* This is going to be hairy! */
__sint kernel_fork()
{
	__uint tnum;

	tnum = alloc_task();
	if (tnum == 0) return -1;
	memcpy((void *)&task[tnum], (void *)&task[CURNUM], sizeof(__task));
	task[tnum].t_num = tnum;  
	task[tnum].t_regs.flags = 0x208;
/* DANGEROUS! */
	task[tnum].t_regs.cs = 0x1002;
	task[tnum].t_regs.ip = (__u16)kernel_fork2;
	arch_segs.lowss -= 0x400; 
	task[tnum].t_regs.ss = arch_segs.lowss; 
	ss1=CURRENT->t_regs.ss;
	ss2=task[tnum].t_regs.ss; 
	sp=task[tnum].t_regs.sp;
#asm
	push ds
	mov bx, #_ss1 
	mov ax, [bx]
	mov dx, ax
	mov bx, #_ss2
	mov ax, [bx]
	mov es, ax
	mov bx, #_sp 
	mov ax, [bx]
	mov ds, dx
	xor cx, cx
	sub cx, ax
	rep
	movsb
	pop ds
#endasm
	/* Playing games with the scheduler - this will make us jump to 
	   kernel_fork2 instead of the scheduler.  (kernel_fork2 will fix this)
	*/
	NEXT = &task[tnum];
	load_regs(NEXT);
	schedule();
	return;	
}

void kernel_fork2()
{
	save_regs(NEXT);
	NEXT->t_regs.ax = NEXT->t_num;
	CURRENT->t_regs = NEXT->t_regs;
	CURRENT->t_regs.ax = 0;
	CURRENT = NEXT; 	/* So we don't upset stack frames! */
	CURNUM = CURRENT->t_num; 
	load_regs(NEXT);
	NEXT = &task[1];	/* Let's get the scheduler back! :) */		
	schedule();
}

/* This is a generic lib function... */

void *memcpy(dest, src, len)
void *dest;
void *src;
__uint len;
{
	int c;

	for (c = 0; c < len; c++)
		((__pu8)dest)[c] = ((__pu8)src)[c];

	return dest;
}

void setup_arch()
{
#asm
	mov bx, #_arch_segs
	mov ax, cs
	mov [bx], ax
	mov [bx+2], di
	mov ax, ss
	mov [bx+8], ax
	mov [bx+6], si
!	This is out of order to save a segment load and a few bytes :)
	mov ax, ds
	mov [bx+4], ax
	mov [bx+10], dx
	xor bx, bx
	mov ds, bx 
	mov [bx+0x200], #_schedjump
	mov [bx+0x202], cs
!	I hate using interrupt space, but 0x300-0x400 is going to be ts data
	mov [bx+0x300], ax
	mov ds, ax
#endasm
	arch_segs.lowss = arch_segs.endss;
}

