;   ####             #    #     # #
;   #   #            #    #       #          The FreeWare C library for
;   #   #  ##   ###  #  # #     # ###             RISC OS machines
;   #   # #  # #     # #  #     # #  #   ___________________________________
;   #   # ####  ###  ##   #     # #  #
;   #   # #        # # #  #     # #  #    Please refer to the accompanying
;   ####   ### ####  #  # ##### # ###    documentation for conditions of use
;   ________________________________________________________________________
;
;   File:    Misc.SWI.s
;   Author:  Copyright  1995 Gareth McCaughan
;   Version: 1.00 (10 Nov 1995)
;   Purpose: Assembler version of DeskLib's SWI veneer.

; on entry r0, r1 are number of registers in/out
;          r2 is SWI number
;          r3, [sp], [sp, #4], ... are input values followed by
;          addresses for output values.

	AREA |D$$Extra|, CODE

call	SWI	0	; For StrongARM
	B	return

	AREA |D$$Code|, CODE, READONLY
	EXPORT Desk_SWI

Desk_SWI	CMP	r0, #10		; too many registers in?
	CMPLS	r1, #10		; too many registers out?
	MOVHIS	pc, lr		; yes, so lose silently
	STMFD	sp!, {r3}	; now all registers at sp
	MOV	ip, sp		; now all registers at ip

	STMFD	sp!, {r1, r4-r9, lr}
	LDR	lr, _call
	TST	r2, #&80000000	; _kernel_swi magic bit meaning not-X
	ORREQ	r2, r2, #&20000		; not set, so set X bit
	ORR	r2, r2, #&EF000000	; SWI (always).
	STR	r2, [lr]		; insert in appropriate place
	MOVS	r0, r0, LSR#1	; C bit indicates whether odd or even
	ADD	r0, r0, r0, LSL#1	; 3*r0
	ADD	pc, pc, r0, LSL#2	; at 12-byte intervals

_call	DCD	call		; this word is going spare...

	LDMCSIA	ip!, {r0}	; +0: 0 or 1
	B	loaded
spare1	DCD	0

	LDMCCIA	ip!, {r0, r1}	; +12: 2 or 3
	LDMCSIA	ip!, {r0-r2}
	B	loaded

	LDMCCIA	ip!, {r0-r3}	; +24: 4 or 5
	LDMCSIA	ip!, {r0-r4}
	B	loaded

	LDMCCIA	ip!, {r0-r5}	; +36: 6 or 7
	LDMCSIA	ip!, {r0-r6}
	B	loaded

	LDMCCIA	ip!, {r0-r7}	; +48: 8 or 9
	LDMCSIA	ip!, {r0-r8}
	B	loaded

	LDMIA	ip!, {r0-r9}	; +60: 10 (or 11, impossible)

loaded	MOV	pc, lr		; lr still contains address of SWI instruction

return	LDMIA	sp!, {lr}	; number of return registers
	RSB	lr, lr, #10	; now it's number of registers NOT to return
	ADD	lr, lr, lr, LSL#1	; *3
	ADD	pc, pc, lr, LSL#2	; *12
spare2	DCD	0

	LDR	lr, [ip, #36]	; +0: 10 registers
	CMP	lr, #0
	STRNE	r9, [lr]
	LDR	lr, [ip, #32]	; +12: 9 registers
	CMP	lr, #0
	STRNE	r8, [lr]
	LDR	lr, [ip, #28]	; +24: 8 registers
	CMP	lr, #0
	STRNE	r7, [lr]
	LDR	lr, [ip, #24]	; +36: 7 registers
	CMP	lr, #0
	STRNE	r6, [lr]
	LDR	lr, [ip, #20]	; +48: 6 registers
	CMP	lr, #0
	STRNE	r5, [lr]
	LDR	lr, [ip, #16]	; +60: 5 registers
	CMP	lr, #0
	STRNE	r4, [lr]
	LDR	lr, [ip, #12]	; +72: 4 registers
	CMP	lr, #0
	STRNE	r3, [lr]
	LDR	lr, [ip, #8]	; +84: 3 registers
	CMP	lr, #0
	STRNE	r2, [lr]
	LDR	lr, [ip, #4]	; +96: 2 registers
	CMP	lr, #0
	STRNE	r1, [lr]
	LDR	lr, [ip, #0]	; +108: 1 register
	CMP	lr, #0
	STRNE	r0, [lr]
				; +120: no registers

	MOVVC	r0, #0		; if no error, return 0
	LDMFD	sp!, {r4-r9, lr}	; done!
	ADD	sp, sp, #4	; skip over r3
	MOVS	pc, lr

	END
