	.level 1.1

#include "common.h"

;!
;! Execution begins here.
;!
;! We are called by the PDC as:
;!
;!	$START$(interactive, endaddr)
;!
;! Where:
;!
;!	interactive - 0 if not interactive, 1 if interactive.
;!
	.section .init
	.EXPORT $START$,entry
	.export _start
$START$:
_start:
	b,n	continue	; This branch instruction may be used
				; by 'palo' to determine whether a boot
				; loader file really is a boot loader, so
				; if you change it palo may start failing.
	.word	0		; This word is available for inserting a
				; checksum when necessary, as on an ISO
				; image where we have to preserve the
				; integrity of this ELF executable file.
	.align 16
	.string PALOMAGIC	; Use strings command to get palo ipl version
	.string PALOVERSION

	.align 16
continue:
	ssm	0,%r0

	;! nuke the W bit, saving original value
	.level 2.0
#define PSW_W_SM	0x200
#define PSW_W_BIT       36
	rsm	PSW_W_SM, %r1

	.level 1.1
	extrw,u	%r1, PSW_W_BIT-32, 1, %r1

	;! find out where we were loaded
	blr	0,%arg1
	ldo	($START$-bbb)(%arg1), %arg1
bbb:
	ldil	L%($global$), %dp
	ldo	R%($global$)(%dp), %dp

	/* find size of first memory module -- assume it is well aligned */
#define IMM_MAX_MEM 0x39c
	ldw	IMM_MAX_MEM(%r0), %r3

	/* due to sign extension problems and PDC, limit RAM to 1G */
	/* this isn't efficient assembly but it doesn't matter here either */
#define ARTIFICIAL_LIMIT (1*1024*1024*1024)
	ldil	L%(ARTIFICIAL_LIMIT), %r4
	ldo	R%(ARTIFICIAL_LIMIT)(%r4), %r4
	cmpb,<<	%r3, %r4, limit_ok
	nop
	copy	%r4, %r3

limit_ok:
	/* stack is at top of initial mem module */
#define STACK_SIZE (64 * 1024)	/* 64 kB */
	ldil	L%(STACK_SIZE), %r4
	ldo	R%(STACK_SIZE)(%r4), %r4

	sub	%r3, %r4, %sp
	copy	%sp, %arg2

	/* heap lives just below stack and grows down -- see iplmain() */

	;! save %sp for calling iplmain()
	copy	%sp, %r10

	;! push a stack frame
	ldo	64(%sp), %sp

	;! save arg0 from the firmware and original PSW-W bit value
	stw	%arg0, -64(%sp)
	stw	%r1, -60(%sp)

	ldil	L%$START$, %arg0
	ldo	R%$START$(%arg0), %arg0

	/* arg2 = _end - $$START$$ */
	.import	_end
	ldil	L%_end, %r5
	ldo	R%_end(%r5), %r5
	sub	%r5, %arg0, %arg2

	;! copy us to our correct destination
	/* memmove($START$, where-loaded, #bytes) */
	bl	memmove, %r2
	nop

	/* restore saved arg0 from firmware and original PSW-W bit */
	ldw	-64(%sp), %arg0
	ldw	-60(%sp), %arg2

	.import iplmain,code
	;! call iplmain via a register so we can jump from PIC space
	;! to non-PIC space
	ldil	L%iplmain, %r3
	ldo	R%iplmain(%r3), %r3

	;! we will "return" to non-PIC space
	ldil	L%ret_iplmain, %r2
	ldo	R%ret_iplmain(%r2), %r2

	;! second arg is initial SP
	copy	%r10, %arg1

call_iplmain:
	;! kernel-entry = iplmain(interactive, free-ptr, started-wide)
	bv	0(%r3)
	nop
ret_iplmain:

	ldil	L%ret_kernel, %r2
	ldo	R%ret_kernel(%r2), %r2

	;! kernel(any-big-number, command-line, rd-start, rd-end)
	ldil	L%commandline, %arg0
	ldo	R%commandline(%arg0), %arg0
	copy	%arg0, %arg1
	ldil	L%(rd_start), %arg2
	ldw	R%(rd_start)(%arg2), %arg2
	ldil	L%(rd_end), %arg3
	ldw	R%(rd_end)(%arg3), %arg3

	bv	0(%ret0)
	nop
	/* shouldn't ever return */
ret_kernel:
	nop
	/* if it does, it won't go past here */
infinite_loop:
	b .
	nop

	.section .init
	.export	memmove
	.export	pa_memcpy
	/* void *memmove(void * dest,const void *src,size_t count)
	 *
	 * combines overkill cache flushing with copying because
	 * this loop is used to copy the bootloader to its final
	 * location where it will then be executed.  You probably
	 * shouldn't use this for generic C memory copying.
	 *
	 * note that memmove() can handle overlapping copies, but
	 * that's not currently used because copying a running program
	 * on top of itself is unwise.
	 */
memmove:
pa_memcpy:
	copy		%arg0, %ret0		/* arg0 must be returned */
	cmpb,<<,n	%arg1, %arg0, mmreverse
	ldo		-1(%arg2), %arg2
	cmpib,=,n	-1, %arg2, mmret	/* if count==0 exit */
mmloop1:    
	ldb,ma		1(%arg1), %r19
	ldo		-1(%arg2), %arg2
	stb		%r19, 0(%arg0)
	fdc		0(%arg0)
	fic		0(%sr0,%arg0)
	ldo		1(%arg0), %arg0
	cmpib,<>	-1, %arg2, mmloop1
	nop
	bv,n		%r0(%rp)
mmreverse:    
	add,l		%arg0, %arg2, %arg0
	add,l		%arg1, %arg2, %arg1
	ldo		-1(%arg2), %arg2
	ldo		-1(%arg0), %arg0
	cmpib,=,n	-1, %arg2, mmret
mmloop2:    
	ldb,mb		-1( %arg1), %r19
	ldo		-1(%arg2), %arg2
	stb		%r19, 0(%arg0)
	fdc		0(%arg0)
	fic		0(%sr0,%arg0)
	ldo		-1(%arg0), %arg0
	cmpib,<>	-1, %arg2, mmloop2
	nop
mmret:	
	bv,n		%r0(%rp)
	nop

/************************ 64-bit real-mode calls ***********************/
/* Called in narrow mode; switches to wide mode for PDC or IODC call */
#define REG_SZ 8
	.text
	.export real64_call_asm
	.level 2.0
	/* unsigned int real64_call_asm(unsigned long long *sp,
	 *		unsigned long long *arg0p,
	 *		unsigned long long fn)
	 *	sp is value of stack pointer to adopt before calling PDC
	 *	arg0p points to where saved arg values may be found
	 *	fn is the PDC/IODC function to call
	 */
real64_call_asm:
	stw	%rp, -20(%sp)		/* save RP */
	stw	%sp, -8(%arg0)		/* save SP on real-mode stack */
	copy	%arg0, %sp		/* adopt the real-mode SP */

	/* switch to wide mode */
1:	mfia	%ret0			/* clear upper part of pcoq */
	ldo	2f-1b(%ret0), %ret0
	depdi	0, 31, 32, %ret0
	bv	(%ret0)
	ssm	0x200, %r3		/* set W-bit */
2:

	copy	%arg2, %r31		/* save fn */
	depd	%arg3, 31, 32, %r31	/* merge in hi part */

	ldo	-2*REG_SZ(%sp), %r29	/* set up the new ap */

	/* load up the arg registers from the saved arg area */
	/* 64-bit calling convention passes first 8 args in registers */
	ldw	0*REG_SZ+4(%arg1), %arg0	/* note overwriting arg0 */
	ldw	2*REG_SZ+4(%arg1), %arg2
	ldw	3*REG_SZ+4(%arg1), %arg3
	ldw	4*REG_SZ+4(%arg1), %r22
	ldw	5*REG_SZ+4(%arg1), %r21
	ldw	6*REG_SZ+4(%arg1), %r20
	ldw	7*REG_SZ+4(%arg1), %r19
	ldw	1*REG_SZ+4(%arg1), %arg1	/* do this one last! */

	ldil	L%r64_ret, %rp
	ldo	R%r64_ret(%rp), %rp
	bv	0(%r31)
	nop

r64_ret:
	mtsm	%r3			/* return to narrow mode */

	ldw	-8(%sp), %sp		/* restore SP */
	ldw	-20(%sp), %rp		/* restore RP */
	bv	0(%rp)
	nop


	/* setjmp and longjmp, ignores FPU and signals */
	.level 1.1

	.export setjmp, code
setjmp:
	stw	%r3, 0(%r26)
	stw	%r4, 8(%r26)
	stw	%r5, 12(%r26)
	stw	%r6, 16(%r26)
	stw	%r7, 20(%r26)
	stw	%r8, 24(%r26)
	stw	%r9, 28(%r26)
	stw	%r10, 32(%r26)
	stw	%r11, 36(%r26)
	stw	%r12, 40(%r26)
	stw	%r13, 44(%r26)
	stw	%r14, 48(%r26)
	stw	%r15, 52(%r26)
	stw	%r16, 56(%r26)
	stw	%r17, 60(%r26)
	stw	%r18, 64(%r26)
	stw	%r19, 68(%r26)
	stw	%r27, 72(%r26)
	stw	%r30, 76(%r26)

	stw	%rp, 80(%r26)
	copy	%r0, %r28
	bv,n	%r0(%rp)


	.export longjmp, code
longjmp:
	ldi	1, %r28

	ldw	0(%r26), %r3
	ldw	8(%r26), %r4
	ldw	12(%r26), %r5
	ldw	16(%r26), %r6
	ldw	20(%r26), %r7
	ldw	24(%r26), %r8
	ldw	28(%r26), %r9
	ldw	32(%r26), %r10
	ldw	36(%r26), %r11
	ldw	40(%r26), %r12
	ldw	44(%r26), %r13
	ldw	48(%r26), %r14
	ldw	52(%r26), %r15
	ldw	56(%r26), %r16
	ldw	60(%r26), %r17
	ldw	64(%r26), %r18
	ldw	68(%r26), %r19
	ldw	72(%r26), %r27
	ldw	76(%r26), %r30

	ldw	80(%r26), %rp
	bv,n	%r0(%rp)


	.data
	.align 4
	.export $global$, data
$global$:
	.word	4
	.import commandline,data
	.export rd_start,data
	.export rd_end,data
rd_start:
	.blockz 4
rd_end:
	.blockz 4

	.section	.bss
	.export real_stack
	.align	64
real_stack:
	.block	8192
