#include <linux16/types.h>
#include <linux16/sched.h>
#include <arch/system.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... but other functions
   can override this and just call schedule().    
*/

__arch_mminit arch_segs;

#asm
_schedjump:
	cli
	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 cx, _CURNUM
	cmp cx, [si]
	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
	nop
	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()
{
/* This is *hopefully* faster than int 0x80 */
#asm
	pop ax
	pushf
	push cs
	push ax
	jmp _schedjump
#endasm
}

void save_regs(task)
__ptask task;
{
#asm
	push bp
	mov bp, sp
	push si
	push di
	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 di
	pop si
	pop bp
#endasm
}

void load_regs(task)
__ptask task;
{
#asm
	push bp
	mov bp, sp
	push si
	push di
	mov si, sp
	mov di, ss
	mov bx, 4[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, 4[bp]
	mov [bx+2], sp
	mov sp, si
	pop di
	pop si
	pop bp
#endasm
}

/* This sets up registers for an in-kernel task - it is recoded in asm 
 * to save memory, since we don't really need it in C since the registers
 * structure should be stable.  If it's not, pull out the asm and the
 * comment and build this...

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;
}

This is a hand-tuned version of that code...
*/

#asm
export	_arch_setupregs
_arch_setupregs:
push	bp
mov	bp,sp
push	di
push	si
mov	bx,4[bp] 	! BCC's code does this redundantly 12 times!
xor	ax,ax
mov	$A[bx],ax
mov	8[bx],ax
mov	6[bx],ax
mov	4[bx],ax
mov	$C[bx], ax
mov 	$E[bx], ax
mov 	$14[bx], ax
mov 	[bx], ax	
mov	ax,[_arch_segs]
mov	$18[bx],ax
mov	ax,[_arch_segs+4]
mov	$10[bx],ax
mov	$12[bx],ax
mov	ax,[_arch_segs+8]
mov	2[bx],ax
pop	si
pop	di
pop	bp
ret
#endasm

extern void schedule_task();

void arch_setuptasks()
{
	__u16 c, ca;

/* 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;
	for (c = 1; c <=2 ; c++) {
		ca = build_task();	/* Assume 1 is returned here */
		while (ca != c) {};
		arch_setupregs(&task[c].t_regs);
		arch_segs.lowss -= 0x400;
		task[c].t_regs.ss = arch_segs.lowss;
		task[c].t_regs.cs = 0x1002;
		task[c].t_regs.flags = 0x208;
	}
	task[1].t_regs.ip = (__u16)schedule_task; 
	task[2].t_regs.ip = (__u16)kfork_task;
	CURRENT = &task[0];
	load_regs(&task[1]);
	/* The fork task is loaded by kernel_fork */
}

void * memcpy();

void *memcpy(dest, src, len)
void *dest;
void *src;
__uint len;
{
	register 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;
}

