/*
 *  (c) Copyright 1986 HEWLETT-PACKARD COMPANY
 *
 *  To anyone who acknowledges that this file is provided "AS IS"
 *  without any express or implied warranty:
 *      permission to use, copy, modify, and distribute this file
 *  for any purpose is hereby granted without fee, provided that
 *  the above copyright notice and this notice appears in all
 *  copies, and that the name of Hewlett-Packard Company not be
 *  used in advertising or publicity pertaining to distribution
 *  of the software without specific, written prior permission.
 *  Hewlett-Packard Company makes no representations about the
 *  suitability of this software for any purpose.
 */

/* HPUX_ID = "@(#) $Revision$" */
/* strlen(s): Return length of string s */

#include <picolibc.h>

#define start   arg0
#define end     ret0
#define tmp1    arg1
#define tmp2    arg2

#include "DEFS.h"

ENTRY(strlen)
        movb,=,n        start,end,$null_ptr
        depi            0,31,2,end
        comb,<>         start,end,$not_aligned
        ldws,ma         4(end),tmp1
        comib,tr        0,0,$loop       /* avoid INDIGO two register interlock */
        uxor,nbz        0,tmp1,0
$not_aligned:
        /*
        ;       Tricky code.  The problem is that the value of of the word
        ;       including the start of the string has some garbage bytes that
        ;       may be 0.  We don't want them to stop the string scan.  So
        ;       we make those bytes non-zero (and any old non-zero value
        ;       will do).  Notice that the end pointer has been rounded
        ;       down to a word boundary, and then incremented to the next
        ;       word by the time we get here.  Therefore, (start-end) has
        ;       one of the values (-3, -2, or -1).  Use uaddcm to do the
        ;       subtraction (instead of sub), and the result will be
        ;       (-4, -3, or -2).  Multiply this by 8, and put into the
        ;       shift register (which truncates to the last 5 bits) and
        ;       the value will be (0, 8, or 16).  Use this as a bit position,
        ;       and drop a mask down into tmp1.  All the garbage bytes will
        ;       have at least 1 bit affected by the vdepi, so all the garbage
        ;       in this first word will be non-zero garbage.
        */
        uaddcm          start,end,tmp2  /*  tmp2 <- {  -4,  -3,  -2 } */
        sh3add          tmp2,0,tmp2     /*  tmp2 <- { -32, -24, -16 } */
        mtsar           tmp2            /*  sar  <- {   0,   8,  16 } */
        vdepi           -1,32,tmp1
        uxor,nbz        0,tmp1,0
$loop:
        b,n             $end_loop
        ldws,ma         4(end),tmp1
        comib,tr        0,0,$loop       /* avoid INDIGO two register interlock */
        uxor,nbz        0,tmp1,0
$end_loop:
        /*       adjust the end pointer to one past the end of the string */
        extru,<>        tmp1,7,8,0
        addib,tr,n      -3,end,$out
        extru,<>        tmp1,15,8,0
        addib,tr,n      -2,end,$out
        extru,<>        tmp1,23,8,0
        addi            -1,end,end
$out:
        bv              0(rp)
        /*
        ;       tricky code.  the end pointer is just beyond the terminating
        ;       null byte, so the length is (end-start-1).  use uaddcm
        ;       to do this in 1 instruction
        */
        uaddcm          end,start,ret0

$null_ptr:
EXIT(strlen)
