;
;  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
;
;  Use of this source code is governed by a BSD-style license
;  that can be found in the LICENSE file in the root of the source
;  tree. An additional intellectual property rights grant can be found
;  in the file PATENTS.  All contributing project authors may
;  be found in the AUTHORS file in the root of the source tree.
;


    EXPORT |vp8_start_encode|
    EXPORT |vp8_encode_bool|
    EXPORT |vp8_stop_encode|
    EXPORT |vp8_encode_value|
    IMPORT |vp8_validate_buffer_arm|

    INCLUDE asm_enc_offsets.asm

    ARM
    REQUIRE8
    PRESERVE8

    AREA    |.text|, CODE, READONLY

    ; macro for validating write buffer position
    ; needs vp8_writer in r0
    ; start shall not be in r1
    MACRO
    VALIDATE_POS $start, $pos
    push {r0-r3, r12, lr}        ; rest of regs are preserved by subroutine call
    ldr  r2, [r0, #vp8_writer_buffer_end]
    ldr  r3, [r0, #vp8_writer_error]
    mov  r1, $pos
    mov  r0, $start
    bl   vp8_validate_buffer_arm
    pop  {r0-r3, r12, lr}
    MEND

; r0 BOOL_CODER *br
; r1 unsigned char *source
; r2 unsigned char *source_end
|vp8_start_encode| PROC
    str     r2,  [r0, #vp8_writer_buffer_end]
    mov     r12, #0
    mov     r3,  #255
    mvn     r2,  #23
    str     r12, [r0, #vp8_writer_lowvalue]
    str     r3,  [r0, #vp8_writer_range]
    str     r12, [r0, #vp8_writer_value]
    str     r2,  [r0, #vp8_writer_count]
    str     r12, [r0, #vp8_writer_pos]
    str     r1,  [r0, #vp8_writer_buffer]
    bx      lr
    ENDP

; r0 BOOL_CODER *br
; r1 int bit
; r2 int probability
|vp8_encode_bool| PROC
    push    {r4-r10, lr}

    mov     r4, r2

    ldr     r2, [r0, #vp8_writer_lowvalue]
    ldr     r5, [r0, #vp8_writer_range]
    ldr     r3, [r0, #vp8_writer_count]

    sub     r7, r5, #1                  ; range-1

    cmp     r1, #0
    mul     r6, r4, r7                  ; ((range-1) * probability)

    mov     r7, #1
    add     r4, r7, r6, lsr #8          ; 1 + (((range-1) * probability) >> 8)

    addne   r2, r2, r4                  ; if  (bit) lowvalue += split
    subne   r4, r5, r4                  ; if  (bit) range = range-split

    ; Counting the leading zeros is used to normalize range.
    clz     r6, r4
    sub     r6, r6, #24                 ; shift

    ; Flag is set on the sum of count.  This flag is used later
    ; to determine if count >= 0
    adds    r3, r3, r6                  ; count += shift
    lsl     r5, r4, r6                  ; range <<= shift
    bmi     token_count_lt_zero         ; if(count >= 0)

    sub     r6, r6, r3                  ; offset = shift - count
    sub     r4, r6, #1                  ; offset-1
    lsls    r4, r2, r4                  ; if((lowvalue<<(offset-1)) & 0x80000000 )
    bpl     token_high_bit_not_set

    ldr     r4, [r0, #vp8_writer_pos]   ; x
    sub     r4, r4, #1                  ; x = w->pos-1
    b       token_zero_while_start
token_zero_while_loop
    mov     r9, #0
    strb    r9, [r7, r4]                ; w->buffer[x] =(unsigned char)0
    sub     r4, r4, #1                  ; x--
token_zero_while_start
    cmp     r4, #0
    ldrge   r7, [r0, #vp8_writer_buffer]
    ldrb    r1, [r7, r4]
    cmpge   r1, #0xff
    beq     token_zero_while_loop

    ldr     r7, [r0, #vp8_writer_buffer]
    ldrb    r9, [r7, r4]                ; w->buffer[x]
    add     r9, r9, #1
    strb    r9, [r7, r4]                ; w->buffer[x] + 1
token_high_bit_not_set
    rsb     r4, r6, #24                 ; 24-offset
    ldr     r9, [r0, #vp8_writer_buffer]
    lsr     r7, r2, r4                  ; lowvalue >> (24-offset)
    ldr     r4, [r0, #vp8_writer_pos]   ; w->pos
    lsl     r2, r2, r6                  ; lowvalue <<= offset
    mov     r6, r3                      ; shift = count
    add     r1, r4, #1                  ; w->pos++
    bic     r2, r2, #0xff000000         ; lowvalue &= 0xffffff
    str     r1, [r0, #vp8_writer_pos]
    sub     r3, r3, #8                  ; count -= 8

    VALIDATE_POS r9, r1                 ; validate_buffer at pos

    strb    r7, [r9, r4]                ; w->buffer[w->pos++]

token_count_lt_zero
    lsl     r2, r2, r6                  ; lowvalue <<= shift

    str     r2, [r0, #vp8_writer_lowvalue]
    str     r5, [r0, #vp8_writer_range]
    str     r3, [r0, #vp8_writer_count]
    pop     {r4-r10, pc}
    ENDP

; r0 BOOL_CODER *br
|vp8_stop_encode| PROC
    push    {r4-r10, lr}

    ldr     r2, [r0, #vp8_writer_lowvalue]
    ldr     r5, [r0, #vp8_writer_range]
    ldr     r3, [r0, #vp8_writer_count]

    mov     r10, #32

stop_encode_loop
    sub     r7, r5, #1                  ; range-1

    mov     r4, r7, lsl #7              ; ((range-1) * 128)

    mov     r7, #1
    add     r4, r7, r4, lsr #8          ; 1 + (((range-1) * 128) >> 8)

    ; Counting the leading zeros is used to normalize range.
    clz     r6, r4
    sub     r6, r6, #24                 ; shift

    ; Flag is set on the sum of count.  This flag is used later
    ; to determine if count >= 0
    adds    r3, r3, r6                  ; count += shift
    lsl     r5, r4, r6                  ; range <<= shift
    bmi     token_count_lt_zero_se      ; if(count >= 0)

    sub     r6, r6, r3                  ; offset = shift - count
    sub     r4, r6, #1                  ; offset-1
    lsls    r4, r2, r4                  ; if((lowvalue<<(offset-1)) & 0x80000000 )
    bpl     token_high_bit_not_set_se

    ldr     r4, [r0, #vp8_writer_pos]   ; x
    sub     r4, r4, #1                  ; x = w->pos-1
    b       token_zero_while_start_se
token_zero_while_loop_se
    mov     r9, #0
    strb    r9, [r7, r4]                ; w->buffer[x] =(unsigned char)0
    sub     r4, r4, #1                  ; x--
token_zero_while_start_se
    cmp     r4, #0
    ldrge   r7, [r0, #vp8_writer_buffer]
    ldrb    r1, [r7, r4]
    cmpge   r1, #0xff
    beq     token_zero_while_loop_se

    ldr     r7, [r0, #vp8_writer_buffer]
    ldrb    r9, [r7, r4]                ; w->buffer[x]
    add     r9, r9, #1
    strb    r9, [r7, r4]                ; w->buffer[x] + 1
token_high_bit_not_set_se
    rsb     r4, r6, #24                 ; 24-offset
    ldr     r9, [r0, #vp8_writer_buffer]
    lsr     r7, r2, r4                  ; lowvalue >> (24-offset)
    ldr     r4, [r0, #vp8_writer_pos]   ; w->pos
    lsl     r2, r2, r6                  ; lowvalue <<= offset
    mov     r6, r3                      ; shift = count
    add     r1, r4, #1                  ; w->pos++
    bic     r2, r2, #0xff000000         ; lowvalue &= 0xffffff
    str     r1, [r0, #vp8_writer_pos]
    sub     r3, r3, #8                  ; count -= 8

    VALIDATE_POS r9, r1                 ; validate_buffer at pos

    strb    r7, [r9, r4]                ; w->buffer[w->pos++]

token_count_lt_zero_se
    lsl     r2, r2, r6                  ; lowvalue <<= shift

    subs    r10, r10, #1
    bne     stop_encode_loop

    str     r2, [r0, #vp8_writer_lowvalue]
    str     r5, [r0, #vp8_writer_range]
    str     r3, [r0, #vp8_writer_count]
    pop     {r4-r10, pc}

    ENDP

; r0 BOOL_CODER *br
; r1 int data
; r2 int bits
|vp8_encode_value| PROC
    push    {r4-r12, lr}

    mov     r10, r2

    ldr     r2, [r0, #vp8_writer_lowvalue]
    ldr     r5, [r0, #vp8_writer_range]
    ldr     r3, [r0, #vp8_writer_count]

    rsb     r4, r10, #32                 ; 32-n

    ; v is kept in r1 during the token pack loop
    lsl     r1, r1, r4                  ; r1 = v << 32 - n

encode_value_loop
    sub     r7, r5, #1                  ; range-1

    ; Decisions are made based on the bit value shifted
    ; off of v, so set a flag here based on this.
    ; This value is refered to as "bb"
    lsls    r1, r1, #1                  ; bit = v >> n
    mov     r4, r7, lsl #7              ; ((range-1) * 128)

    mov     r7, #1
    add     r4, r7, r4, lsr #8          ; 1 + (((range-1) * 128) >> 8)

    addcs   r2, r2, r4                  ; if  (bit) lowvalue += split
    subcs   r4, r5, r4                  ; if  (bit) range = range-split

    ; Counting the leading zeros is used to normalize range.
    clz     r6, r4
    sub     r6, r6, #24                 ; shift

    ; Flag is set on the sum of count.  This flag is used later
    ; to determine if count >= 0
    adds    r3, r3, r6                  ; count += shift
    lsl     r5, r4, r6                  ; range <<= shift
    bmi     token_count_lt_zero_ev      ; if(count >= 0)

    sub     r6, r6, r3                  ; offset = shift - count
    sub     r4, r6, #1                  ; offset-1
    lsls    r4, r2, r4                  ; if((lowvalue<<(offset-1)) & 0x80000000 )
    bpl     token_high_bit_not_set_ev

    ldr     r4, [r0, #vp8_writer_pos]   ; x
    sub     r4, r4, #1                  ; x = w->pos-1
    b       token_zero_while_start_ev
token_zero_while_loop_ev
    mov     r9, #0
    strb    r9, [r7, r4]                ; w->buffer[x] =(unsigned char)0
    sub     r4, r4, #1                  ; x--
token_zero_while_start_ev
    cmp     r4, #0
    ldrge   r7, [r0, #vp8_writer_buffer]
    ldrb    r11, [r7, r4]
    cmpge   r11, #0xff
    beq     token_zero_while_loop_ev

    ldr     r7, [r0, #vp8_writer_buffer]
    ldrb    r9, [r7, r4]                ; w->buffer[x]
    add     r9, r9, #1
    strb    r9, [r7, r4]                ; w->buffer[x] + 1
token_high_bit_not_set_ev
    rsb     r4, r6, #24                 ; 24-offset
    ldr     r9, [r0, #vp8_writer_buffer]
    lsr     r7, r2, r4                  ; lowvalue >> (24-offset)
    ldr     r4, [r0, #vp8_writer_pos]   ; w->pos
    lsl     r2, r2, r6                  ; lowvalue <<= offset
    mov     r6, r3                      ; shift = count
    add     r11, r4, #1                 ; w->pos++
    bic     r2, r2, #0xff000000         ; lowvalue &= 0xffffff
    str     r11, [r0, #vp8_writer_pos]
    sub     r3, r3, #8                  ; count -= 8

    VALIDATE_POS r9, r11                ; validate_buffer at pos

    strb    r7, [r9, r4]                ; w->buffer[w->pos++]

token_count_lt_zero_ev
    lsl     r2, r2, r6                  ; lowvalue <<= shift

    subs    r10, r10, #1
    bne     encode_value_loop

    str     r2, [r0, #vp8_writer_lowvalue]
    str     r5, [r0, #vp8_writer_range]
    str     r3, [r0, #vp8_writer_count]
    pop     {r4-r12, pc}
    ENDP

    END
