| @ 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 |
| |
| 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 |