;	$NetBSD: start.S,v 1.1 2014/02/24 07:23:43 skrll Exp $

; Copyright (c) 2003 ITOH Yasufumi.
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 1. Redistributions of source code must retain the above copyright
;    notice, this list of conditions and the following disclaimer.
; 2. Redistributions in binary forms are unlimited.
;
; THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS''
; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
; BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
; THE POSSIBILITY OF SUCH DAMAGE.

	.level	1.0

	.code
	.origin	0
	;
	; LIF (Logical Interchange Format) header
	;
lifhdr:	.byte	0x80,0x00	; LIF magic
	.string	"NetBSD"	; volume label (6 chars, fill with space)
	.origin	0xf0
	; 0xf0
lif_ipl_addr:
	.word	top-lifhdr	; start at 4KB (must be 2KB aligned)
lif_ipl_size:
	.word	0x00001000	; size 4KB (must be 2KB aligned)
lif_ipl_entry:
	.word	$START$-top	; entry offset

	; ipl part 1 starts here
	.origin	4096
top:
	;
	; IPL startup
	;
	; arg0 = interact flag (1: interactive, 0: otherwise)
	; arg1 = address of first doubleword past the end of ipl part 1
	;
	.export $START$,entry
$START$:
	b,n	start			; 0: entry address

; version of interface of primary to secondary boot
BOOT_IF_VERSION:	.equ	0
; version 0: arg0 = interact flag, arg1 = version (0),
;	     arg2 = end addr, arg3 = selected boot partition
;	     r1, r3 - r22, r28, r29, r31 = cleared to zeros

cksum:		.word	0		; 4: checksum will be stored here
version:	.word	BOOT_IF_VERSION	; 8: version of interface
rsvd1:		.word	0		; 12: future use
rsvd2:		.word	0		; 16: future use
rsvd3:		.word	0		; 20: future use

start:
	; set data pointer for relocatable data access
	blr	%r0,%r27
	; get PSW at entry
	ssm	0,%r4
	.export	$global$,data
$global$:

	; save parameters for main
	copy	%arg0,%r3

tmpdiskbufsz:		.equ	0x1000	; 4KB
tmpdiskbuf_labelsec:	.equ	0x0200	; dbtob(LABELSECTOR)
tmpdiskbuf_labelsecsz:	.equ	512
tmpdiskbuf_part2:	.equ	0x0400
tmpdiskbuf_part2sz:	.equ	0x0400
tmpdiskbuf_part3:	.equ	0x0A00
tmpdiskbuf_part3sz:	.equ	0x0600

part1sz:		.equ	0x1000

	; get next free address
	.import	_end,data
	addil	L%_end-$global$,%r27;%r1
	ldo	R%_end-$global$(%r1),%r1

	; 32bit environment (and this code) requires stack is 64byte aligned.
	ldi	64-1,%r2
	add	%r1,%r2,%r1
	andcm	%r1,%r2,%r6			; r6 = tmp disk buffer
	ldo	tmpdiskbufsz+64(%r6),%sp	; tmp stack

	bl	print,%rp
	ldo	str_startup-$global$(%r27),%arg0

	;
	; read part 2 and 3 of ipl (see README.ipl)
	;

	; read disk blocks which contains ipl part 2 and part 3
	copy	%r6,%arg0
	ldi	0,%arg2			; offset = 0
	bl	boot_input,%rp
	ldi	tmpdiskbufsz,%arg1	; read size

	; part 2 address
	ldo	top-$global$+part1sz(%r27),%r19

	; copy part 2
	ldo	tmpdiskbuf_part2(%r6),%r20
	addi,tr	tmpdiskbuf_part2sz/4,%r0,%r2	; loop count, skip next
cpipl2:	stws,ma		%r1,4(0,%r19)		; write to dst
	addib,uv,n	-1,%r2,cpipl2		; check loop condition
	ldws,ma		4(0,%r20),%r1		; read from src

	; copy part 3
	; (r19 already has destination address of part 3)
	ldo	tmpdiskbuf_part3(%r6),%r20
	addi,tr	tmpdiskbuf_part3sz/4,%r0,%r2	; loop count, skip next
cpipl3:	stws,ma		%r1,4(0,%r19)		; write to dst
	addib,uv,n	-1,%r2,cpipl3		; check loop condition
	ldws,ma		4(0,%r20),%r1		; read from src

	; flush data cache / invalidate instruction cache
	ldo	top-$global$+part1sz(%r27),%r1	; part 2 address
	ldi	16,%r20		; 16: cache line size
flipl:	fdc	0(0,%r1)	; flush data cache line at r1
	comb,<	%r1,%r19,flipl
	fic,m	%r20(0,%r1)	; flush instruction cache line at r1, r1 += 16
	sync			; I/O operation is guaranteed to finish
				; in eight instructions after sync
	;
	; Now, whole the IPL is loaded
	;

	; clear BSS
	.import	_edata,data
	addil	L%_edata-$global$,%r27;%r1
	ldo	R%_edata-$global$(%r1),%r1
clrbss:	comb,<	%r1,%r6,clrbss
	stws,ma	%r0,4(0,%r1)

	; we have read disklabel -- save it for later use
	.import	labelsector,data
	addil	L%labelsector-$global$,%r27;%r1
	ldo	R%labelsector-$global$(%r1),%r20
	ldo	tmpdiskbuf_labelsec(%r6),%r21
	addi,tr	tmpdiskbuf_labelsecsz/4,%r0,%r2	; loop count, skip next
cplbl:	stws,ma		%r1,4(0,%r20)		; write to dst
	addib,uv,n	-1,%r2,cplbl		; check loop condition
	ldws,ma		4(0,%r21),%r1		; read from src

	; set stack
	; (r6 points at free space, 64byte aligned)
	; 32bit environment (and this code) requires stack is 64byte aligned.
	ldo	64(%r6),%sp	; 64 > 48: frame marker (32) + args(up to 4)

	; stack usage
	;	12bytes	arguments
	;	32	frame marker

	; parameters for main
	copy	%r3,%arg0
	copy	%r4,%arg2

	.import	ipl_main,entry
	bl	ipl_main,%rp
	copy	%sp,%arg1

	; main returned --- perform reset
	bl	print,%rp
	ldo	str_reset-$global$(%r27),%arg0
	; FALLTHROUGH

IOMOD_CMD:		.equ	0xFFFC0000 + (4*12)
IOMOD_CMD_STOP:		.equ	0
IOMOD_CMD_RESET:	.equ	5

; void reboot(void)
; void halt(void)
	.export reboot,entry
	.export halt,entry
reboot:
	addi,tr	IOMOD_CMD_RESET,%r0,%r1	; %r1 = IOMOD_CMD_RESET, skip next
halt:	ldi	IOMOD_CMD_STOP,%r1
iomcmd:	ldil	L%IOMOD_CMD,%r2
	ldo	R%IOMOD_CMD(%r2),%r2
	stwas	%r1,0(%r2)
	b,n	.

str_startup:
	.string		"\r\n\n"
	.stringz	"NetBSD/hppa FFS/LFS Primary Bootstrap\r\n\n"
str_reset:
	.stringz	"\r\nresetting..."
	.align	4

; void dispatch(unsigned interactive, unsigned top, unsigned end, int part,
;		unsigned entry)
	.export	dispatch,entry
dispatch:
	; flush data cache / invalidate instruction cache
	ldi	16,%r20		; 16: cache line size
flush:	fdc	0(0,%arg1)	; flush data cache line at arg1
	comb,<	%arg1,%arg2,flush
	fic,m	%r20(0,%arg1)	; flush instruction cache line at arg1, arg1+=16
	sync
	copy	%r0,%r1		; I/O operation is guaranteed to finish
	copy	%r0,%r3		; in eight instructions after sync
	copy	%r0,%r4
	copy	%r0,%r5		; while waiting, clear unused registers
	copy	%r0,%r6		; for future compatibility
	copy	%r0,%r7
	copy	%r0,%r8
	copy	%r0,%r9
	copy	%r0,%r10
	copy	%r0,%r11
	copy	%r0,%r12
	copy	%r0,%r13
	copy	%r0,%r14
	copy	%r0,%r15
	copy	%r0,%r16
	copy	%r0,%r17
	copy	%r0,%r18
	copy	%r0,%r19
	copy	%r0,%r20
	copy	%r0,%r21
	copy	%r0,%r22
	copy	%r0,%r28	; r23-r26: arg3-arg0, r27: dp
	copy	%r0,%r29	; r30: sp
	copy	%r0,%r31
	ldw	-52(%sp),%arg1			; arg4: exec address
	ldo	reboot-$global$(%r27),%rp	; reboot if returns
	bv	%r0(%arg1)			; execute
	ldi	BOOT_IF_VERSION,%arg1

;
;	IODC subroutines
;
PZ_MEM_CONSOLE:		.equ	0x3a0
PZ_MEM_BOOT:		.equ	0x3d0
PZ_MEM_KEYBOARD:	.equ	0x400

DEV_PATH:		.equ	0x00
DEV_LAYERS:		.equ	0x08
DEV_HPA:		.equ	0x20	; hard physical address space
DEV_SPA:		.equ	0x24	; soft physical address space
DEV_IODC_ENTRY:		.equ	0x28
DEV_CLASS:		.equ	0x2c
DEV_CL_DUPLEX:		.equ	0x7	; full-duplex console class

IODC_ENTRY_IO_BOOTIN:		.equ	0
IODC_ENTRY_IO_CONSOLEIN:	.equ	2
IODC_ENTRY_IO_CONSOLEOUT:	.equ	3

; call_iodc
; inputs:
;	%ret0	IODC base in page zero
;	%rp	return address
;	%r29	arg 8
;	%r19	arg 7
;	%r20	arg 6
;	%r21	arg 5
;	%r25	arg 1
; outputs
;	all scratch regs	undefined, unless defined by the IODC call
call_iodc:
	; set common arguments in registers
	addil	L%retbuf-$global$,%r27;%r1
	ldo	R%retbuf-$global$(%r1),%r22	; arg4: return buffer
	ldo	DEV_LAYERS(%ret0),%arg3		; arg3: layer
	ldw	DEV_SPA(%ret0),%arg2		; arg2: spa
	ldw	DEV_HPA(%ret0),%arg0		; arg0: hpa
	; check if narrow or wide mode
	ssm	0,%r1				; get PSW
	bb,<	%r1,4,call_iodc_64		; if W, call in 64bit mode
	ldw	DEV_IODC_ENTRY(%ret0),%r1	; ENTRY_IO address

	; narrow mode
	stw	%r29,-68(%sp)			; arg8: maxsize / lang
	stw	%r19,-64(%sp)			; arg7: size
	stw	%r20,-60(%sp)			; arg6: buf
	stw	%r21,-56(%sp)			; arg5: devaddr / unused
	bv	%r0(%r1)			; call ENTRY_IO
	stw	%r22,-52(%sp)			; arg4: return buffer

call_iodc_64:
	.allow	2.0
	; On PA64 convention, arg0 - arg7 are passed in registers.
	; Parameters are placed in INCREASING order.
	; The argument pointer points at the first stack parameter.
	; stack usage:
	;	64bytes	allocated for register arguments arg0-arg7
	;	 8	arg8 (argument pointer points here)
	;	16	frame marker
	std	%r29,-16-8(%sp)			; arg8: maxsize / lang
;	std	%sp,-8(%sp)			; psp in frame marker
	bv	%r0(%r1)			; call ENTRY_IO
	ldo	-16-8(%sp),%r29			; argument pointer
	.allow

;
; console output
;
; void putch(int)
; void print(const char *string)
	.align	4
	.export putch,entry
	.export print,entry
putch:
	stwm	%arg0,128(%sp)		; fake up a string on the stack
	stb	%r0,-124(%sp)		; (see stack usage below)
	addi,tr	-125,%sp,%arg0		; string address, skip next
print:
	.proc
	.callinfo	frame=128,save_rp,no_unwind
	.entry
	ldo	128(%sp),%sp
	stw	%rp,-128-20(%sp)

	; stack usage:
	;	36byte	IODC buffer (assume %sp was 64byte aligned)
	;	 4	saved reg
	;	88	arguments, frame marker
	;		32bit: 36 (arguments) + 32 (frame marker)
	;		64bit: 72 (arguments) + 16 (frame marker)
prbufsiz:	.equ	36

	; save callee-saves
	stw	%r3,-92(%sp)

	copy	%arg0,%r3

prloop:
	copy	%r0,%r19
	ldi	prbufsiz,%r20
	ldo	-128(%sp),%r1

strloop:
	ldb	0(%r3),%r2
	comb,=	%r2,%r0,endstr
	stbs,ma	%r2,1(0,%r1)
	ldo	1(%r19),%r19
	comb,<>	%r19,%r20,strloop
	ldo	1(%r3),%r3

endstr:
	comb,=,n	%r19,%r0,endpr

	; see IODC 3-51
	; arg0 hpa
	; arg1 option (ENTRY_IO_CONSOLEOUT (3))
	; arg2 spa
	; arg3 ID_addr (pointer to LAYER)
	; arg4 R_addr (pointer to return buffer (64word?))
	; arg5 unused (0)
	; arg6 memaddr (64byte-aligned) string buffer
	; arg7 reqsize
	; arg8 lang (0)
	ldi	PZ_MEM_CONSOLE,%ret0		; IODC base in page zero
	copy	%r0,%r29			; arg8: lang
;	copy	%r19,%r19			; arg7: size
	ldo	-128(%sp),%r20			; arg6: buf
;	copy	%r0,%r21			; arg5: unused
	bl	call_iodc,%rp
	ldi	IODC_ENTRY_IO_CONSOLEOUT,%arg1	; arg1: option
	b,n	prloop

endpr:
	; restore callee-saves
	ldw	-92(%sp),%r3

	; return subroutine
	ldw	-128-20(%sp),%rp
	bv	%r0(%rp)
	.exit
	ldo	-128(%sp),%sp
	.procend

;
; console input
;
; int getch(void)
	.align	4
	.export	getch,entry
getch:
	.proc
	.callinfo	frame=192,save_rp,no_unwind
	.entry
	stw	%rp,-20(%sp)
	ldo	192(%sp),%sp

	; stack usage:
	;	64byte	IODC buffer (assume %sp was 64byte aligned)
	;	40	unused
	;	88	arguments, frame marker
	;		32bit: 36 (arguments) + 32 (frame marker)
	;		64bit: 72 (arguments) + 16 (frame marker)

	; check if console is full or half duplex
	ldw	PZ_MEM_CONSOLE+DEV_CLASS(%r0),%r1	; device class
	extru	%r1,31,4,%r1				; right 4bits are valid
	ldi	PZ_MEM_CONSOLE,%ret0
	comib,=,n	DEV_CL_DUPLEX,%r1,getch_console	; use CONSOLE if full
	ldi	PZ_MEM_KEYBOARD,%ret0			; otherwise KEYBOARD
getch_console:

	; see IODC 3-50
	; arg0 hpa
	; arg1 option (ENTRY_IO_CONSOLEIN (2))
	; arg2 spa
	; arg3 ID_addr (pointer to LAYER)
	; arg4 R_addr (pointer to return buffer (64word?))
	; arg5 unused (0)
	; arg6 memaddr (64byte-aligned, must have 64byte) data buffer
	; arg7 reqsize
	; arg8 lang (0)
;	copy	%rp,%rp				; IODC base in page zero
	copy	%r0,%r29			; arg8: lang
	ldi	1,%r19				; arg7: size (1)
	ldo	-192(%sp),%r20			; arg6: buf
;	copy	%r0,%r21			; arg5: unused
	bl	call_iodc,%rp
	ldi	IODC_ENTRY_IO_CONSOLEIN,%arg1	; arg1: option

	; make return value
	comb,<>	%ret0,%r0,getch_ret		; return -1 on error
	ldi	-1,%ret0
	ldi	1,%r19

	; check if narrow or wide mode
	ssm	0,%r1				; get PSW
	bb,<	%r1,4,getch_64
	addil	L%retbuf-$global$,%r27;%r1
	ldw	R%retbuf-$global$(%r1),%r2	; ret[0]
	comclr,<>	%r19,%r2,%ret0		; return 0 if no char available
getch_retc:
	ldb	-192(%sp),%ret0			;  otherwise return the char

getch_ret:
	; return subroutine
	ldw	-192-20(%sp),%rp
	bv	%r0(%rp)
	.exit
	ldo	-192(%sp),%sp

getch_64:
	.allow	2.0
	ldd	R%retbuf-$global$(%r1),%r2	; ret[0] (64bit)
	b	getch_retc
	cmpclr,*<>	%r19,%r2,%ret0		; return 0 if no char available
	.allow
	.procend

;
; read boot device
;
; void boot_input(void *buf, unsigned len, unsigned pos)
	.align	4
	.export boot_input,entry
boot_input:
	.proc
	.callinfo	frame=128,save_rp,no_unwind
	.entry
	stw	%rp,-20(%sp)
	ldo	128(%sp),%sp

	; stack usage:
	;	40byte	unused (alignment)
	;	88	arguments, frame marker
	;		32bit: 36 (arguments) + 32 (frame marker)
	;		64bit: 72 (arguments) + 16 (frame marker)

	; see IODC 3-46
	; arg0 hpa
	; arg1 option (ENTRY_IO_BOOTIN (0))
	; arg2 spa
	; arg3 ID_addr (pointer to LAYER)
	; arg4 R_addr (pointer to return buffer (64word?))
	; arg5 devaddr
	; arg6 memaddr (64byte-aligned) string buffer
	; arg7 reqsize
	; arg8 maxsize
	ldi	PZ_MEM_BOOT,%ret0		; IODC base in page zero
	copy	%arg1,%r29			; arg8: maxsize
	copy	%arg1,%r19			; arg7: size
	copy	%arg0,%r20			; arg6: buf
	copy	%arg2,%r21			; arg5: devaddr
	bl	call_iodc,%rp
	ldi	IODC_ENTRY_IO_BOOTIN,%arg1	; arg1: option

	; return subroutine
	ldw	-128-20(%sp),%rp
	bv	%r0(%rp)
	.exit
	ldo	-128(%sp),%sp
	.procend

;
;	utilities
;	optimized for size
;

; int strcmp(const char *str1, const char *str2)
	.align	4
	.export		strcmp,entry
strcmp:
	.proc
	.callinfo	frame=0,no_calls
	.entry
	ldbs,ma		1(0,%arg0),%r1
strcmp_loop:
	comb,=		%r1,%r0,strcmp_eos
	ldbs,ma		1(0,%arg1),%r19
	comb,=,n	%r1,%r19,strcmp_loop
	ldbs,ma		1(0,%arg0),%r1
strcmp_eos:
	bv		%r0(%rp)
	.exit
	sub		%r1,%r19,%ret0
	.procend

; void memcpy(void *dst, const void *src, unsigned len)
	.align	4
	.export		memcpy,entry
	.export		memmove,entry
memcpy:
memmove:
	.proc
	.callinfo	no_unwind		; multiple exit points
	.entry
;	copy		%arg0,%ret0		; uncomment this to conform ANSI
	comb,<<,n	%arg0,%arg1,memcpy0	; copy forward or backward?
	add		%arg0,%arg2,%arg0	; dst end address
	add,tr		%arg1,%arg2,%arg1	; src end address, skip next
memcpy_bwd:
	stbs,mb		%r1,-1(0,%arg0)		; write to dst
	addib,uv,n	-1,%arg2,memcpy_bwd	; check loop condition
	ldbs,mb		-1(0,%arg1),%r1		; read from src
	bv,n		%r0(%rp)		; return subroutine
memcpy_fwd:
	stbs,ma		%r1,1(0,%arg0)		; write to dst
memcpy0:
	addib,uv,n	-1,%arg2,memcpy_fwd	; check loop condition
	ldbs,ma		1(0,%arg1),%r1		; read from src
	.exit
	bv,n		%r0(%rp)		; return subroutine
	.procend

;
;	string table
;	placed here to save space
;
	.export	str_seekseq, data
	.export	str_startup, data
	.export	str_bit_firmware, data
	.export	str_crlf, data
	.export	str_space, data
	.export	str_rubout, data
str_seekseq:
	.stringz	"repositioning media...\r\n"
str_bit_firmware:
	.stringz	"bit firmware\r\n"
str_rubout:
	.byte		0x08, 0x20, 0x08, 0x00	; "\b \b"

	.export	str_bootpart, data
str_bootpart:
	.string		"boot partition (a-p, ! to reboot) [a]:"
str_space:
	.stringz	" "
	.export	str_booting_part, data
str_booting_part:
	.string		"\r\nbooting from partition _"
str_crlf:
	.stringz	"\r\n"
	.export	str_warn_2GB, data
str_warn_2GB:
	.stringz	"boot partition exceeds 2GB boundary\r\n"
	.export	str_warn_unused, data
str_warn_unused:
	.stringz	"unused partition\r\n"
	.export	str_nolabel, data
str_nolabel:
	.stringz	"no disklabel\r\n"

	.export	str_filesystem, data
str_filesystem:
	.stringz	"filesystem: _FS\r\n"
	.export	str_nofs, data
str_nofs:
	.stringz	"no filesystem found\r\n"
	.export	str_lookup, data
	.export	str_loading, data
	.export	str_at, data
	.export	str_dddot, data
	.export	str_done, data
str_lookup:
	.stringz	"looking up "
str_loading:
	.stringz	"loading "
str_at:
	.stringz	" at 0x"
str_dddot:
	.stringz	"..."
str_done:
	.stringz	"done\r\n"

	.export	str_boot1, data
	.export	str_boot2, data
	.export	str_boot3, data
str_boot1:
	.stringz	"boot.hp700"
str_boot2:
	.stringz	"boot"
str_boot3:
	.stringz	"usr/mdec/boot"

	.export	str_noboot, data
str_noboot:
	.stringz	"no secondary boot found\r\n"

	.export	str_ukfmt, data
str_ukfmt:
	.stringz	": unknown format -- exec from top\r\n"

	.bss
	.align	64
retbuf:	.block	32*8		; *4 for narrow mode / *8 for wide mode

	.export diskbuf,data
	.align	64
diskbuf:
	.block	2048
