;
; oxswi.s
;
; Original version of _swi[x] SWI veneers
;
;  1994 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's C library stubs (xstubs).
;
; xstubs is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; xstubs is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with xstubs.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

		GET	libs:stream

;----- Main code ------------------------------------------------------------

		AREA	|C$$code|,CODE,READONLY

; --- _swi, _swix ---
;
; On entry:	R0 == SWI number
;		R1 == flags
;		R2,R3 == arguments
;		More arguments on stack
;
; On exit:	R0 == output register, if any
;
; Use:		Generalised SWI veneer, compatible with Acorn's latest.

		EXPORT	|_swi|
		EXPORT	|_swix|

swi		ROUT

|_swi|		MOV	R12,R13			;Keep stack pointer safe
		STMFD	R13!,{R2,R3}		;Save variadic args on stack
		STMFD	R13!,{R4-R12,R14}	;Save loads of other regs
		ADR	R10,%20swi		;Go to the SWI return pt
		B	%00swi			;Do the main job thing

|_swix|		MOV	R12,R13			;Keep stack pointer safe
		STMFD	R13!,{R2,R3}		;Save variadic args on stack
		STMFD	R13!,{R4-R12,R14}	;Save loads of other regs
		ORR	R0,R0,#&20000		;Set the SWI's X bit
		BIC	R1,R1,#&000F0000	;Make it return R0
		ADR	R10,%10swi		;Go to the SWIX return pt

		; --- Build entry stack frame ---

00swi		MOV	R2,R1,LSL #22		;Get the input registers
		MOVS	R2,R2,LSR #22
		ORRNE	R2,R2,#&E8000000	;Build that as an LDMIA
		ORRNE	R2,R2,#&00BC0000	;Based on R12 (ip)

		ANDS	R3,R1,#&800		;Does he want a local block?
		BLNE	%80swi			;Yes -- better find it then

		ORR	R4,R0,#&EF000000	;Build the SWI instruction

		MOV	R6,#&E1000000		;Build a MOV skeleton
		ORR	R6,R6,#&00A00000

		AND	R5,R1,#&000F0000	;Get return register value
		ORR	R5,R6,R5,LSR #16	;Put in the source register
		ORR	R5,R5,#&0000E000	;Put in destination

		ORR	R6,R6,#&0000F000	;Set up return instruction
		ORR	R6,R6,#&0000000A

		SUB	R12,R12,#8		;Find variadic arguments
		STMFD	R13!,{R2-R6}		;Build SWI-calling frame

		BARRIER
		MOV	R11,R1			;Look after the flags word
		MOV	PC,R13			;And call the stack code

		; --- Process a return from the SWI ---

10swi		MOVVC	R14,#0			;If no error, return 0
20swi		STR	PC,[R13,#0]		;Save my program counter

		; --- Output the output registers ---

		MOVS	R11,R11,LSL #1		;Shift bits to C and N
		LDRCS	R10,[R12],#4		;If we write R0, find addr
		STRCS	R0,[R10,#0]		;And write it out
		LDRMI	R10,[R12],#4		;If we write R1, find addr
		STRMI	R1,[R10,#0]		;And write it out

		MOVS	R11,R11,LSL #2		;Move next two bits out
		LDRCS	R10,[R12],#4		;If we write R2, find addr
		STRCS	R2,[R10,#0]		;And write it out
		LDRMI	R10,[R12],#4		;If we write R3, find addr
		STRMI	R3,[R10,#0]		;And write it out
		TST	R11,#&7F000000		;Any more to do?
		BEQ	%30swi			;No -- save 16 cycles

		MOVS	R11,R11,LSL #2		;Move next two bits out
		LDRCS	R10,[R12],#4		;If we write R4, find addr
		STRCS	R4,[R10,#0]		;And write it out
		LDRMI	R10,[R12],#4		;If we write R5, find addr
		STRMI	R5,[R10,#0]		;And write it out

		MOVS	R11,R11,LSL #2		;Move next two bits out
		LDRCS	R10,[R12],#4		;If we write R6, find addr
		STRCS	R6,[R10,#0]		;And write it out
		LDRMI	R10,[R12],#4		;If we write R7, find addr
		STRMI	R7,[R10,#0]		;And write it out

		MOVS	R11,R11,LSL #2		;Move next two bits out
		LDRCS	R10,[R12],#4		;If we write R8, find addr
		STRCS	R8,[R10,#0]		;And write it out
		LDRMI	R10,[R12],#4		;If we write R9, find addr
		STRMI	R9,[R10,#0]		;And write it out

		TST	R11,#&40000000		;Is the `write PC' bit set?
		LDRNE	R0,[R13,#0]		;Yes -- load the one I saved
		LDRNE	R10,[R12],#4		;Load the address
		STRNE	R0,[R10,#0]		;And write the PC value out

30swi		ADD	R13,R13,#20		;Restore the stack pointer
		MOV	R0,R14			;Get the return value
		LDMFD	R13!,{R4-R11,R13,PC}^	;And return to caller

		; --- Work out address of literal block ---

80swi		MOVS	R4,R1,LSL #1		;Look after flags word
		MOV	R5,#6			;Simple counter thingy
		AND	R3,R1,#&0000F000	;Get the block register
		ORR	R3,R3,#&E2000000	;Build into ADD instruction
		ORR	R3,R3,#&008C0000

85swi		ADDCS	R3,R3,#4		;Move pointer on for each reg
		ADDMI	R3,R3,#4
		SUBS	R5,R5,#1		;Decrement counter too
		MOVLE	PC,R14			;If finished, return
		MOVS	R4,R4,LSL #2		;Shift next two bits out
		B	%85swi			;And loop round again

		LTORG

;----- That's all, folks ----------------------------------------------------

		END
