; IDEA cipher implemented in ARM code
; (c) 1997 Philipp Hullmann <hullmann@nano.fkp.uni-hannover.de>

	GET	s.arm_regs

	AREA	|C$$code|, CODE, READONLY
	IMPORT	|x$stack_overflow|

;------------------------------------------------------------------------
; void arm_ideaCipher(byte const inbuf[8], byte outbuf[8],
;			word16 const *key);

	EXPORT	arm_ideaCipher

aicpn_s	DCB 	"arm_ideaCipher", 0
	ALIGN	4
aicpn_e	DCD	&ff000000 + aicpn_e - aicpn_s

arm_ideaCipher
	MOV	ip, sp			; ip now points to the fifth argument
	STMFD	sp!, {v1, v2, v3, v4, v5, v6, fp, ip, lr, pc}
	SUB	fp, ip, #4		; fp points to the saved pc
	CMP	sp, sl 			; Test for stack overflow
	BLLT	|x$stack_overflow|

	; WARNING: This code assumes that the input buffer starts at
	; a word boundary. This is the case with current pgp code, but
	; may not be the case in different contexts, or even with compilers
	; other than Norcroft C 3.00.

	LDR	a4, [a1], #4
	AND	ip, a4, #&ff00		; x1
	MOV	v1, ip, LSR #8
	AND	ip, a4, #&ff
	ORR	v1, v1, ip, LSL #8
	AND	ip, a4, #&ff000000	; x2
	MOV	v2, ip, LSR #24
	AND 	ip, a4, #&ff0000
	ORR	v2, v2, ip, LSR #8
	LDR	a4, [a1]
	AND	ip, a4, #&ff00		; x3
	MOV	v3, ip, LSR #8
	AND	ip, a4, #&ff
	ORR	v3, v3, ip, LSL #8
	AND	ip, a4, #&ff000000	; x4
	MOV	v4, ip, LSR #24
	AND 	ip, a4, #&ff0000
	ORR	v4, v4, ip, LSR #8

	; registers:
	; a1: pointer to inbuf and loop counter
	; a2: pointer to outbuf and scratch
	; a3: pointer to key
	; a4: key word32
	; v1: x1
	; v2: x2
	; v3: x3
	; v4: x4
	; v5: s2
	; v6: s3
	; ip: scratch

	STMFD	sp!, {a2}		; push outbuf
	MOV	a1, #8			; r = IDEAROUNDS
	TST	a3, #2			; check if key is word-aligned
	BNE	aic_not_aligned

aic_aligned
	LDR	a4, [a3], #4		; MUL(x1, *key++)
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v1, v1, LSL #16		; x = low16(x)
	MOV	v1, v1, LSR #16
	MULS	ip, v1, a2		; p = a * b
        BEQ	aim_p_zero_1
	MOV	v1, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v1, v1, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v1, v1, #1
	B	aim_end_1
aim_p_zero_1
	CMP	v1, #0
	RSBNE	v1, v1, #1		; if (a) return 1 - a
	RSBEQ	v1, a2, #1		; else return 1 - b
aim_end_1

	ADD	v2, v2, a4, LSR #16

	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	ADD	v3, v3, a2, LSR #16

	MOV	a2, a4, LSR #16		; MUL(x4, *key++)
	MOV	v4, v4, LSL #16		; x = low16(x)
	MOV	v4, v4, LSR #16
	MULS	ip, v4, a2		; p = a * b
        BEQ	aim_p_zero_2
	MOV	v4, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v4, v4, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v4, v4, #1
	B	aim_end_2
aim_p_zero_2
	CMP	v4, #0
	RSBNE	v4, v4, #1		; if (a) return 1 - a
	RSBEQ	v4, a2, #1		; else return 1 - b
aim_end_2

	MOV	v6, v3			; s3 = x3

	EOR	v3, v3, v1		; x3 ^= x1; MUL(x3, *key++)
	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v3, v3, LSL #16		; x = low16(x)
	MOV	v3, v3, LSR #16
	MULS	ip, v3, a2		; p = a * b
        BEQ	aim_p_zero_2a
	MOV	v3, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v3, v3, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v3, v3, #1
	B	aim_end_2a
aim_p_zero_2a
	CMP	v3, #0
	RSBNE	v3, v3, #1		; if (a) return 1 - a
	RSBEQ	v3, a2, #1		; else return 1 - b
aim_end_2a

	MOV	v5, v2			; s2 = x2
	EOR	v2, v2, v4		; x2 ^= x4

	ADD	v2, v2, v3		; x2 += x3; MUL(x2, *key++)
	MOV	a2, a4, LSR #16
	MOV	v2, v2, LSL #16		; x = low16(x)
	MOV	v2, v2, LSR #16
	MULS	ip, v2, a2		; p = a * b
        BEQ	aim_p_zero_3
	MOV	v2, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v2, v2, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v2, v2, #1
	B	aim_end_3
aim_p_zero_3
	CMP	v2, #0
	RSBNE	v2, v2, #1		; if (a) return 1 - a
	RSBEQ	v2, a2, #1		; else return 1 - b
aim_end_3

	ADD	v3, v3, v2		; x3 += x2
	EOR	v1, v1, v2		; x1 ^= x2
	EOR	v4, v4, v3		; x4 ^= x3
	EOR	v2, v2, v6		; x2 ^= s3
	EOR	v3, v3, v5		; x3 ^= s2

	SUBS	a1, a1, #1		; while(--r)
	BNE	aic_aligned		; end of loop

	LDR	a4, [a3], #4		; MUL(x1, *key++)
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v1, v1, LSL #16		; x = low16(x)
	MOV	v1, v1, LSR #16
	MULS	ip, v1, a2		; p = a * b
        BEQ	aim_p_zero_4
	MOV	v1, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v1, v1, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v1, v1, #1
	B	aim_end_4
aim_p_zero_4
	CMP	v1, #0
	RSBNE	v1, v1, #1		; if (a) return 1 - a
	RSBEQ	v1, a2, #1		; else return 1 - b
aim_end_4

	ADD	v3, v3, a4, LSR #16

	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	ADD	v2, v2, a2, LSR #16

	MOV	a2, a4, LSR #16		; MUL(x4, *key)
	MOV	v4, v4, LSL #16		; x = low16(x)
	MOV	v4, v4, LSR #16
	MULS	ip, v4, a2		; p = a * b
        BEQ	aim_p_zero_5
	MOV	v4, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v4, v4, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v4, v4, #1
	B	aim_end_5
aim_p_zero_5
	CMP	v4, #0
	RSBNE	v4, v4, #1		; if (a) return 1 - a
	RSBEQ	v4, a2, #1		; else return 1 - b
aim_end_5
	B	aic_store

aic_not_aligned
	BIC	a3, a3, #2
	LDR	a4, [a3], #4
aic_na_loop
	MOV	a2, a4, LSR #16		; MUL(x1, *key++)
	MOV	v1, v1, LSL #16		; x = low16(x)
	MOV	v1, v1, LSR #16
	MULS	ip, v1, a2		; p = a * b
        BEQ	aim_p_zero_6
	MOV	v1, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v1, v1, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v1, v1, #1
	B	aim_end_6
aim_p_zero_6
	CMP	v1, #0
	RSBNE	v1, v1, #1		; if (a) return 1 - a
	RSBEQ	v1, a2, #1		; else return 1 - b
aim_end_6

	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	ADD	v2, v2, a2, LSR #16

	ADD	v3, v3, a4, LSR #16

	LDR	a4, [a3], #4		; MUL(x4, *key++)
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v4, v4, LSL #16		; x = low16(x)
	MOV	v4, v4, LSR #16
	MULS	ip, v4, a2		; p = a * b
        BEQ	aim_p_zero_7
	MOV	v4, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v4, v4, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v4, v4, #1
	B	aim_end_7
aim_p_zero_7
	CMP	v4, #0
	RSBNE	v4, v4, #1		; if (a) return 1 - a
	RSBEQ	v4, a2, #1		; else return 1 - b
aim_end_7

	MOV	v6, v3			; s3 = x3

	EOR	v3, v3, v1		; x3 ^= x1; MUL(x3, *key++)
	MOV	a2, a4, LSR #16
	MOV	v3, v3, LSL #16		; x = low16(x)
	MOV	v3, v3, LSR #16
	MULS	ip, v3, a2		; p = a * b
        BEQ	aim_p_zero_8
	MOV	v3, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v3, v3, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v3, v3, #1
	B	aim_end_8
aim_p_zero_8
	CMP	v3, #0
	RSBNE	v3, v3, #1		; if (a) return 1 - a
	RSBEQ	v3, a2, #1		; else return 1 - b
aim_end_8

	MOV	v5, v2			; s2 = x2
	EOR	v2, v2, v4		; x2 ^= x4

	ADD	v2, v2, v3		; x2 += x3; MUL(x2, *key++)
	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v2, v2, LSL #16		; x = low16(x)
	MOV	v2, v2, LSR #16
	MULS	ip, v2, a2		; p = a * b
        BEQ	aim_p_zero_9
	MOV	v2, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v2, v2, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v2, v2, #1
	B	aim_end_9
aim_p_zero_9
	CMP	v2, #0
	RSBNE	v2, v2, #1		; if (a) return 1 - a
	RSBEQ	v2, a2, #1		; else return 1 - b
aim_end_9

	ADD	v3, v3, v2		; x3 += x2
	EOR	v1, v1, v2		; x1 ^= x2
	EOR	v4, v4, v3		; x4 ^= x3
	EOR	v2, v2, v6		; x2 ^= s3
	EOR	v3, v3, v5		; x3 ^= s2

	SUBS	a1, a1, #1		; while(--r)
	BNE	aic_na_loop		; end of loop

	MOV	a2, a4, LSR #16		; MUL(x1, *key++)
	MOV	v1, v1, LSL #16		; x = low16(x)
	MOV	v1, v1, LSR #16
	MULS	ip, v1, a2		; p = a * b
        BEQ	aim_p_zero_10
	MOV	v1, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v1, v1, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v1, v1, #1
	B	aim_end_10
aim_p_zero_10
	CMP	v1, #0
	RSBNE	v1, v1, #1		; if (a) return 1 - a
	RSBEQ	v1, a2, #1		; else return 1 - b
aim_end_10

	LDR	a4, [a3], #4
	MOV	a2, a4, LSL #16
	ADD	v3, v3, a2, LSR #16

	ADD	v2, v2, a4, LSR #16

	LDR	a4, [a3], #4		; MUL(x4, *key)
	MOV	a2, a4, LSL #16
	MOV	a2, a2, LSR #16
	MOV	v4, v4, LSL #16		; x = low16(x)
	MOV	v4, v4, LSR #16
	MULS	ip, v4, a2		; p = a * b
        BEQ	aim_p_zero_11
	MOV	v4, ip, LSR #16		; a = p >> 16
	MOV	a2, ip, LSL #16		; b = low16(p)
	RSBS	v4, v4, a2, LSR #16	; return (b - a) + (b < a)
	ADDCC	v4, v4, #1
	B	aim_end_11
aim_p_zero_11
	CMP	v4, #0
	RSBNE	v4, v4, #1		; if (a) return 1 - a
	RSBEQ	v4, a2, #1		; else return 1 - b
aim_end_11

aic_store
	MOV	ip, #&ff		; x1 = low16(xi)
	ORR	ip, ip, #&ff00
	AND	v1, v1, ip
	AND	v2, v2, ip
	AND	v3, v3, ip
	AND	v4, v4, ip

	LDMFD	sp!, {v6}		; pop outbuf
	MOV	a1, v1, LSR #8		; store x1
	STRB	a1, [v6], #1
	AND	a1, v1, #&ff
	STRB	a1, [v6], #1
	MOV	a1, v3, LSR #8		; store x3
	STRB	a1, [v6], #1
	AND	a1, v3, #&ff
	STRB	a1, [v6], #1
	MOV	a1, v2, LSR #8		; store x2
	STRB	a1, [v6], #1
	AND	a1, v2, #&ff
	STRB	a1, [v6], #1
	MOV	a1, v4, LSR #8		; store x4
	STRB	a1, [v6], #1
	AND	a1, v4, #&ff
	STRB	a1, [v6], #1

	LDMEA	fp, {v1, v2, v3, v4, v5, v6, fp, sp, pc}^	; return

	END
