/*
 * Copyright (c) 2013 RISC OS Open Ltd
 * Author: Ben Avison <bavison@riscosopen.org>
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavutil/arm/asm.S"

RESULT  .req    a1
BUF     .req    a1
SIZE    .req    a2
PATTERN .req    a3
PTR     .req    a4
DAT0    .req    v1
DAT1    .req    v2
DAT2    .req    v3
DAT3    .req    v4
TMP0    .req    v5
TMP1    .req    v6
TMP2    .req    ip
TMP3    .req    lr

#define PRELOAD_DISTANCE 4

.macro innerloop4
        ldr             DAT0, [PTR], #4
        subs            SIZE, SIZE, #4 @ C flag survives rest of macro
        sub             TMP0, DAT0, PATTERN, lsr #14
        bic             TMP0, TMP0, DAT0
        ands            TMP0, TMP0, PATTERN
.endm

.macro innerloop16  decrement, do_preload
        ldmia           PTR!, {DAT0,DAT1,DAT2,DAT3}
 .ifnc "\do_preload",""
        pld             [PTR, #PRELOAD_DISTANCE*32]
 .endif
 .ifnc "\decrement",""
        subs            SIZE, SIZE, #\decrement @ C flag survives rest of macro
 .endif
        sub             TMP0, DAT0, PATTERN, lsr #14
        sub             TMP1, DAT1, PATTERN, lsr #14
        bic             TMP0, TMP0, DAT0
        bic             TMP1, TMP1, DAT1
        sub             TMP2, DAT2, PATTERN, lsr #14
        sub             TMP3, DAT3, PATTERN, lsr #14
        ands            TMP0, TMP0, PATTERN
        bic             TMP2, TMP2, DAT2
        it              eq
        andseq          TMP1, TMP1, PATTERN
        bic             TMP3, TMP3, DAT3
        itt             eq
        andseq          TMP2, TMP2, PATTERN
        andseq          TMP3, TMP3, PATTERN
.endm

/* int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size) */
function ff_startcode_find_candidate_armv6, export=1
        push            {v1-v6,lr}
        mov             PTR, BUF
        @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go
        @ before using code that does preloads
        cmp             SIZE, #(PRELOAD_DISTANCE+3)*32 - 1
        blo             60f

        @ Get to word-alignment, 1 byte at a time
        tst             PTR, #3
        beq             2f
1:      ldrb            DAT0, [PTR], #1
        sub             SIZE, SIZE, #1
        teq             DAT0, #0
        beq             90f
        tst             PTR, #3
        bne             1b
2:      @ Get to 4-word alignment, 1 word at a time
        ldr             PATTERN, =0x80008000
        setend          be
        tst             PTR, #12
        beq             4f
3:      innerloop4
        bne             91f
        tst             PTR, #12
        bne             3b
4:      @ Get to cacheline (8-word) alignment
        tst             PTR, #16
        beq             5f
        innerloop16     16
        bne             93f
5:      @ Check complete cachelines, with preloading
        @ We need to stop when there are still (PRELOAD_DISTANCE+1)
        @ complete cachelines to go
        sub             SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32
6:      innerloop16     , do_preload
        bne             93f
        innerloop16     32
        bne             93f
        bcs             6b
        @ Preload trailing part-cacheline, if any
        tst             SIZE, #31
        beq             7f
        pld             [PTR, #(PRELOAD_DISTANCE+1)*32]
        @ Check remaining data without doing any more preloads. First
        @ do in chunks of 4 words:
7:      adds            SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16
        bmi             9f
8:      innerloop16     16
        bne             93f
        bcs             8b
        @ Then in words:
9:      adds            SIZE, SIZE, #16 - 4
        bmi             11f
10:     innerloop4
        bne             91f
        bcs             10b
11:     setend          le
        @ Check second byte of final halfword
        ldrb            DAT0, [PTR, #-1]
        teq             DAT0, #0
        beq             90f
        @ Check any remaining bytes
        tst             SIZE, #3
        beq             13f
12:     ldrb            DAT0, [PTR], #1
        sub             SIZE, SIZE, #1
        teq             DAT0, #0
        beq             90f
        tst             SIZE, #3
        bne             12b
        @ No candidate found
13:     sub             RESULT, PTR, BUF
        b               99f

60:     @ Small buffer - simply check by looping over bytes
        subs            SIZE, SIZE, #1
        bcc             99f
61:     ldrb            DAT0, [PTR], #1
        subs            SIZE, SIZE, #1
        teq             DAT0, #0
        beq             90f
        bcs             61b
        @ No candidate found
        sub             RESULT, PTR, BUF
        b               99f

90:     @ Found a candidate at the preceding byte
        sub             RESULT, PTR, BUF
        sub             RESULT, RESULT, #1
        b               99f

91:     @ Found a candidate somewhere in the preceding 4 bytes
        sub             RESULT, PTR, BUF
        sub             RESULT, RESULT, #4
        sub             TMP0, DAT0, #0x20000
        bics            TMP0, TMP0, DAT0
        itt             pl
        ldrbpl          DAT0, [PTR, #-3]
        addpl           RESULT, RESULT, #2
        bpl             92f
        teq             RESULT, #0
        beq             98f @ don't look back a byte if found at first byte in buffer
        ldrb            DAT0, [PTR, #-5]
92:     teq             DAT0, #0
        it              eq
        subeq           RESULT, RESULT, #1
        b               98f

93:     @ Found a candidate somewhere in the preceding 16 bytes
        sub             RESULT, PTR, BUF
        sub             RESULT, RESULT, #16
        teq             TMP0, #0
        beq             95f @ not in first 4 bytes
        sub             TMP0, DAT0, #0x20000
        bics            TMP0, TMP0, DAT0
        itt             pl
        ldrbpl          DAT0, [PTR, #-15]
        addpl           RESULT, RESULT, #2
        bpl             94f
        teq             RESULT, #0
        beq             98f @ don't look back a byte if found at first byte in buffer
        ldrb            DAT0, [PTR, #-17]
94:     teq             DAT0, #0
        it              eq
        subeq           RESULT, RESULT, #1
        b               98f
95:     add             RESULT, RESULT, #4
        teq             TMP1, #0
        beq             96f @ not in next 4 bytes
        sub             TMP1, DAT1, #0x20000
        bics            TMP1, TMP1, DAT1
        itee            mi
        ldrbmi          DAT0, [PTR, #-13]
        ldrbpl          DAT0, [PTR, #-11]
        addpl           RESULT, RESULT, #2
        teq             DAT0, #0
        it              eq
        subeq           RESULT, RESULT, #1
        b               98f
96:     add             RESULT, RESULT, #4
        teq             TMP2, #0
        beq             97f @ not in next 4 bytes
        sub             TMP2, DAT2, #0x20000
        bics            TMP2, TMP2, DAT2
        itee            mi
        ldrbmi          DAT0, [PTR, #-9]
        ldrbpl          DAT0, [PTR, #-7]
        addpl           RESULT, RESULT, #2
        teq             DAT0, #0
        it              eq
        subeq           RESULT, RESULT, #1
        b               98f
97:     add             RESULT, RESULT, #4
        sub             TMP3, DAT3, #0x20000
        bics            TMP3, TMP3, DAT3
        itee            mi
        ldrbmi          DAT0, [PTR, #-5]
        ldrbpl          DAT0, [PTR, #-3]
        addpl           RESULT, RESULT, #2
        teq             DAT0, #0
        it              eq
        subeq           RESULT, RESULT, #1
        @ drop through to 98f
98:     setend          le
99:     pop             {v1-v6,pc}
endfunc

        .unreq  RESULT
        .unreq  BUF
        .unreq  SIZE
        .unreq  PATTERN
        .unreq  PTR
        .unreq  DAT0
        .unreq  DAT1
        .unreq  DAT2
        .unreq  DAT3
        .unreq  TMP0
        .unreq  TMP1
        .unreq  TMP2
        .unreq  TMP3
