@ Tremolo library
@-----------------------------------------------------------------------
@ Copyright (C) 2002-2009, Xiph.org Foundation
@ Copyright (C) 2010, Robin Watts for Pinknoise Productions Ltd
@ All rights reserved.

@ Redistribution and use in source and binary forms, with or without
@ modification, are permitted provided that the following conditions
@ are met:

@     * Redistributions of source code must retain the above copyright
@ notice, this list of conditions and the following disclaimer.
@     * Redistributions in binary form must reproduce the above
@ copyright notice, this list of conditions and the following disclaimer
@ in the documentation and/or other materials provided with the
@ distribution.
@     * Neither the names of the Xiph.org Foundation nor Pinknoise
@ Productions Ltd nor the names of its contributors may be used to
@ endorse or promote products derived from this software without
@ specific prior written permission.
@
@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
@ OWNER 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.
@ ----------------------------------------------------------------------

    .text

	.global	oggpack_look
	.global	oggpack_adv
	.global	oggpack_readinit
	.global	oggpack_read

	.type	oggpack_look, %function
	.type	oggpack_adv, %function
	.type	oggpack_readinit, %function
	.type	oggpack_read, %function

oggpack_look:
	@ r0 = oggpack_buffer *b
	@ r1 = int             bits
	STMFD	r13!,{r10,r11,r14}
	LDMIA	r0,{r2,r3,r12}
					@ r2 = bitsLeftInSegment
					@ r3 = ptr
					@ r12= bitsLeftInWord
	SUBS	r2,r2,r1		@ bitsLeftinSegment -= bits
	BLT	look_slow		@ Not enough bits in this segment for
					@ this request. Do it slowly.
	LDR	r10,[r3]		@ r10= ptr[0]
	RSB	r14,r12,#32		@ r14= 32-bitsLeftInWord
	SUBS	r12,r12,r1		@ r12= bitsLeftInWord -= bits
	LDRLT	r11,[r3,#4]!		@ r11= ptr[1]
	MOV	r10,r10,LSR r14		@ r10= ptr[0]>>(32-bitsLeftInWord)
	ADDLE	r12,r12,#32		@ r12= bitsLeftInWord += 32
	RSB	r14,r14,#32		@ r14= 32-bitsLeftInWord
	ORRLT	r10,r10,r11,LSL r14	@ r10= Next 32 bits.
	MOV	r14,#1
	RSB	r14,r14,r14,LSL r1
	AND	r0,r10,r14
	LDMFD	r13!,{r10,r11,PC}

look_slow:
	STMFD	r13!,{r5,r6}
	ADDS	r10,r2,r1		@ r10= bitsLeftInSegment + bits (i.e.
					@ the initial value of bitsLeftInSeg)
	@ r10 = bitsLeftInSegment (initial)
	@ r12 = bitsLeftInWord
	RSB	r14,r12,#32		@ r14= 32-bitsLeftInWord
	MOV	r5,r10			@ r5 = bitsLeftInSegment (initial)
	BLT	look_overrun
	BEQ	look_next_segment	@ r10= r12 = 0, if we branch
	CMP	r12,r10			@ If bitsLeftInWord < bitsLeftInSeg
					@ there must be more in the next word
	LDR	r10,[r3],#4		@ r10= ptr[0]
	LDRLT	r6,[r3]			@ r6 = ptr[1]
	MOV	r11,#1
	MOV	r10,r10,LSR r14		@ r10= first bitsLeftInWord bits
	ORRLT	r10,r10,r6,LSL r12	@ r10= first bitsLeftInSeg bits+crap
	RSB	r11,r11,r11,LSL r5	@ r11= mask
	AND	r10,r10,r11		@ r10= first r5 bits
	@ Load the next segments data
look_next_segment:
	@ At this point, r10 contains the first r5 bits of the result
	LDR	r11,[r0,#12]		@ r11= head = b->head
	@ Stall
	@ Stall
look_next_segment_2:
	LDR	r11,[r11,#12]		@ r11= head = head->next
	@ Stall
	@ Stall
	CMP	r11,#0
	BEQ	look_out_of_data
	LDMIA	r11,{r6,r12,r14}	@ r6 = buffer
					@ r12= begin
					@ r14= length
	LDR	r6,[r6]			@ r6 = buffer->data
	CMP	r14,#0
	BEQ	look_next_segment_2
	ADD	r6,r6,r12		@ r6 = buffer->data+begin
look_slow_loop:
	LDRB	r12,[r6],#1		@ r12= *buffer
	SUBS	r14,r14,#1		@ r14= length
	@ Stall
	ORR	r10,r10,r12,LSL r5	@ r10= first r5+8 bits
	ADD	r5,r5,#8
	BLE	look_really_slow
	CMP	r5,r1
	BLT	look_slow_loop
	MOV	r14,#1
	RSB	r14,r14,r14,LSL r1
	AND	r0,r10,r14
	LDMFD	r13!,{r5,r6,r10,r11,PC}


look_really_slow:
	CMP	r5,r1
	BLT	look_next_segment_2
	MOV	r14,#1
	RSB	r14,r14,r14,LSL r1
	AND	r0,r10,r14
	LDMFD	r13!,{r5,r6,r10,r11,PC}

look_out_of_data:
	@MVN	r0,#0			; return -1
	MOV	r0,#0
	LDMFD	r13!,{r5,r6,r10,r11,PC}

look_overrun:
	@ We had overrun when we started, so we need to skip -r10 bits.
	LDR	r11,[r0,#12]		@ r11 = head = b->head
	@ stall
	@ stall
look_overrun_next_segment:
	LDR	r11,[r11,#12]		@ r11 = head->next
	@ stall
	@ stall
	CMP	r11,#0
	BEQ	look_out_of_data
	LDMIA	r11,{r6,r7,r14}		@ r6 = buffer
					@ r7 = begin
					@ r14= length
	LDR	r6,[r6]			@ r6 = buffer->data
	@ stall
	@ stall
	ADD	r6,r6,r7		@ r6 = buffer->data+begin
	MOV	r14,r14,LSL #3		@ r14= length in bits
	ADDS	r14,r14,r10		@ r14= length in bits-bits to skip
	MOVLE	r10,r14
	BLE	look_overrun_next_segment
	RSB	r10,r10,#0		@ r10= bits to skip
	ADD	r6,r10,r10,LSR #3	@ r6 = pointer to data
	MOV	r10,#0
	B	look_slow_loop

oggpack_adv:
	@ r0 = oggpack_buffer *b
	@ r1 = bits
	LDMIA	r0,{r2,r3,r12}
					@ r2 = bitsLeftInSegment
					@ r3 = ptr
					@ r12= bitsLeftInWord
	SUBS	r2,r2,r1		@ Does this run us out of bits in the
	BLE	adv_slow		@ segment? If so, do it slowly
	SUBS	r12,r12,r1
	ADDLE	r12,r12,#32
	ADDLE	r3,r3,#4
	STMIA	r0,{r2,r3,r12}
	BX      LR
adv_slow:
	STMFD	r13!,{r10,r14}

	LDR	r14,[r0,#12]		@ r14= head
	@ stall
adv_slow_loop:
	LDR	r1,[r0,#20]		@ r1 = count
	LDR	r10,[r14,#8]		@ r10= head->length
	LDR	r14,[r14,#12]		@ r14= head->next
	@ stall
	ADD	r1,r1,r10		@ r1 = count += head->length
	CMP	r14,#0
	BEQ	adv_end
	STR	r1,[r0,#20]		@ b->count = count
	STR	r14,[r0,#12]		@ b->head = head
	LDMIA	r14,{r3,r10,r12}	@ r3 = buffer
					@ r10= begin
					@ r12= length
	LDR	r3,[r3]			@ r3 = buffer->data
	ADD	r3,r3,r10		@ r3 = Pointer to start (byte)
	AND	r10,r3,#3		@ r10= bytes to backtrk to word align
	MOV	r10,r10,LSL #3		@ r10= bits to backtrk to word align
	RSB	r10,r10,#32		@ r10= bits left in word
	ADDS	r10,r10,r2		@ r10= bits left in word after skip
	ADDLE	r10,r10,#32
	ADDLE	r3,r3,#4
	BIC	r3,r3,#3		@ r3 = Pointer to start (word)
	ADDS	r2,r2,r12,LSL #3	@ r2 = length in bits after advance
	BLE	adv_slow_loop
	STMIA	r0,{r2,r3,r10}

	LDMFD	r13!,{r10,PC}
adv_end:
	MOV	r2, #0
	MOV	r12,#0
	STMIA	r0,{r2,r3,r12}

	LDMFD	r13!,{r10,PC}

oggpack_readinit:
	@ r0 = oggpack_buffer *b
	@ r1 = oggreference   *r
	STR	r1,[r0,#12]		@ b->head = r1
	STR	r1,[r0,#16]		@ b->tail = r1
	LDMIA	r1,{r2,r3,r12}		@ r2 = b->head->buffer
					@ r3 = b->head->begin
					@ r12= b->head->length
	LDR	r2,[r2]			@ r2 = b->head->buffer->data
	MOV	r1,r12,LSL #3		@ r1 = BitsInSegment
	MOV	r12,#0
	ADD	r3,r2,r3		@ r3 = r2+b->head->begin
	BIC	r2,r3,#3		@ r2 = b->headptr (word)
	AND	r3,r3,#3
	MOV	r3,r3,LSL #3
	RSB	r3,r3,#32		@ r3 = BitsInWord
	STMIA	r0,{r1,r2,r3}
	STR	r12,[r0,#20]
	BX      LR

oggpack_read:
	@ r0 = oggpack_buffer *b
	@ r1 = int             bits
	STMFD	r13!,{r10,r11,r14}
	LDMIA	r0,{r2,r3,r12}
					@ r2 = bitsLeftInSegment
					@ r3 = ptr
					@ r12= bitsLeftInWord
	SUBS	r2,r2,r1		@ bitsLeftinSegment -= bits
	BLT	read_slow		@ Not enough bits in this segment for
					@ this request. Do it slowly.
	LDR	r10,[r3]		@ r10= ptr[0]
	RSB	r14,r12,#32		@ r14= 32-bitsLeftInWord
	SUBS	r12,r12,r1		@ r12= bitsLeftInWord -= bits
	ADDLE	r3,r3,#4
	LDRLT	r11,[r3]		@ r11= ptr[1]
	MOV	r10,r10,LSR r14		@ r10= ptr[0]>>(32-bitsLeftInWord)
	ADDLE	r12,r12,#32		@ r12= bitsLeftInWord += 32
	RSB	r14,r14,#32		@ r14= 32-bitsLeftInWord
	ORRLT	r10,r10,r11,LSL r14	@ r10= Next 32 bits.
	STMIA	r0,{r2,r3,r12}
	MOV	r14,#1
	RSB	r14,r14,r14,LSL r1
	AND	r0,r10,r14
	LDMFD	r13!,{r10,r11,PC}

read_slow:
	STMFD	r13!,{r5,r6}
	ADDS	r10,r2,r1		@ r10= bitsLeftInSegment + bits (i.e.
					@ the initial value of bitsLeftInSeg)
	@ r10 = bitsLeftInSegment (initial)
	@ r12 = bitsLeftInWord
	RSB	r14,r12,#32		@ r14= 32-bitsLeftInWord
	MOV	r5,r10			@ r5 = bitsLeftInSegment (initial)
	BLT	read_overrun
	BEQ	read_next_segment	@ r10= r12 = 0, if we branch
	CMP	r12,r10			@ If bitsLeftInWord < bitsLeftInSeg
					@ there must be more in the next word
	LDR	r10,[r3],#4		@ r10= ptr[0]
	LDRLT	r6,[r3]			@ r6 = ptr[1]
	MOV	r11,#1
	MOV	r10,r10,LSR r14		@ r10= first bitsLeftInWord bits
	ORRLT	r10,r10,r6,LSL r12	@ r10= first bitsLeftInSeg bits+crap
	RSB	r11,r11,r11,LSL r5	@ r11= mask
	AND	r10,r10,r11		@ r10= first r5 bits
	@ Load the next segments data
read_next_segment:
	@ At this point, r10 contains the first r5 bits of the result
	LDR	r11,[r0,#12]		@ r11= head = b->head
	@ Stall
read_next_segment_2:
	@ r11 = head
	LDR	r6,[r0,#20]		@ r6 = count
	LDR	r12,[r11,#8]		@ r12= length
	LDR	r11,[r11,#12]		@ r11= head = head->next
	@ Stall
	ADD	r6,r6,r12		@ count += length
	CMP	r11,#0
	BEQ	read_out_of_data
	STR	r11,[r0,#12]
	STR	r6,[r0,#20]		@ b->count = count
	LDMIA	r11,{r6,r12,r14}	@ r6 = buffer
					@ r12= begin
					@ r14= length
	LDR	r6,[r6]			@ r6 = buffer->data
	CMP	r14,#0
	BEQ	read_next_segment_2
	ADD	r6,r6,r12		@ r6 = buffer->data+begin
read_slow_loop:
	LDRB	r12,[r6],#1		@ r12= *buffer
	SUBS	r14,r14,#1		@ r14= length
	@ Stall
	ORR	r10,r10,r12,LSL r5	@ r10= first r5+8 bits
	ADD	r5,r5,#8
	BLE	read_really_slow
	CMP	r5,r1
	BLT	read_slow_loop
read_end:
	MOV	r12,#1
	RSB	r12,r12,r12,LSL r1

	@ Store back the new position
	@ r2 = -number of bits to go from this segment
	@ r6 = ptr
	@ r14= bytesLeftInSegment
	@ r11= New head value
	LDMIA	r11,{r3,r6,r14}		@ r3 = buffer
					@ r6 = begin
					@ r14= length
	LDR	r3,[r3]			@ r3 = buffer->data
	ADD	r1,r2,r14,LSL #3	@ r1 = bitsLeftInSegment
	@ stall
	ADD	r6,r3,r6		@ r6 = pointer
	AND	r3,r6,#3		@ r3 = bytes used in first word
	RSB	r3,r2,r3,LSL #3		@ r3 = bits used in first word
	BIC	r2,r6,#3		@ r2 = word ptr
	RSBS	r3,r3,#32		@ r3 = bitsLeftInWord
	ADDLE	r3,r3,#32
	ADDLE	r2,r2,#4
	STMIA	r0,{r1,r2,r3}

	AND	r0,r10,r12
	LDMFD	r13!,{r5,r6,r10,r11,PC}


read_really_slow:
	CMP	r5,r1
	BGE	read_end
	LDR	r14,[r11,#8]		@ r14= length of segment just done
	@ stall
	@ stall
	ADD	r2,r2,r14,LSL #3	@ r2 = -bits to use from next seg
	B	read_next_segment_2

read_out_of_data:
	@ Store back the new position
	@ r2 = -number of bits to go from this segment
	@ r6 = ptr
	@ r14= bytesLeftInSegment
	@ RJW: This may be overkill - we leave the buffer empty, with -1
	@ bits left in it. We might get away with just storing the
	@ bitsLeftInSegment as -1.
	LDR	r11,[r0,#12]		@ r11=head

	LDMIA	r11,{r3,r6,r14}		@ r3 = buffer
					@ r6 = begin
					@ r14= length
	LDR	r3,[r3]			@ r3 = buffer->data
	ADD	r6,r3,r6		@ r6 = pointer
	ADD	r6,r6,r14
	AND	r3,r6,#3		@ r3 = bytes used in first word
	MOV	r3,r3,LSL #3		@ r3 = bits used in first word
	BIC	r2,r6,#3		@ r2 = word ptr
	RSBS	r3,r3,#32		@ r3 = bitsLeftInWord
	MVN	r1,#0			@ r1 = -1 = bitsLeftInSegment
	STMIA	r0,{r1,r2,r3}
	@MVN	r0,#0			; return -1
	MOV	r0,#0
	LDMFD	r13!,{r5,r6,r10,r11,PC}

read_overrun:
	@ We had overrun when we started, so we need to skip -r10 bits.
	LDR	r11,[r0,#12]		@ r11 = head = b->head
	@ stall
	@ stall
read_overrun_next_segment:
	LDR	r11,[r11,#12]		@ r11 = head->next
	@ stall
	@ stall
	CMP	r11,#0
	BEQ	read_out_of_data
	LDMIA	r11,{r6,r7,r14}		@ r6 = buffer
					@ r7 = begin
					@ r14= length
	LDR	r6,[r6]			@ r6 = buffer->data
	@ stall
	@ stall
	ADD	r6,r6,r7		@ r6 = buffer->data+begin
	MOV	r14,r14,LSL #3		@ r14= length in bits
	ADDS	r14,r14,r10		@ r14= length in bits-bits to skip
	MOVLE	r10,r14
	BLE	read_overrun_next_segment
	RSB	r10,r10,#0		@ r10= bits to skip
	ADD	r6,r10,r10,LSR #3	@ r6 = pointer to data
	MOV	r10,#0
	B	read_slow_loop

	@ END
