{ $Id$ This file is part of the Free Pascal run time library. Copyright (c) 2000 by Jonas Maebe, member of the Free Pascal development team Processor dependent part of strings.pp, that can be shared with sysutils unit. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program 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. **********************************************************************} { Note: the implementation of these routines is for BIG ENDIAN only!! (JM) } function strcopy(dest,source : pchar) : pchar;assembler; { in: dest in r3, source in r4 } { out: result (dest) in r3 } asm { empty/invalid string? } cmpli r3,0 { if yes, do nothing } beq .LStrCopyDone { clear two lowest bits of source address } rlwminm r28,r4,0,0,31-2 { get # of misaligned bytes } sub. r28,r28,r4 { since we have to return dest intact, use another register for } { dest in the copy loop } mr r29,r3 beq .LStrCopyAligned .LStrCopyAlignLoop: { decrease misaligned bytes counter (do it here already to improve } { jump prediction) } subic. r28,1 { load next byte } lbz r27,(r4) { end of string? } cmpli cr1,r27,0 { point to next source byte } addi r4,r4,1 { store byte } stb r27,(r29) { point to next dest address } addi r29,r29,1 { stop if end of string } beq cr1,.LStrCopyDone bne .LStrCopyAlignLoop .balign 16 .LStrCopyAligned: { load next 4 bytes } lwz r27,(r4) { first/highest byte zero? (big endian!) } andis. r28,r27,0x0ff00 addi r4,r4,4 beq .LStrCopyByte { second byte zero? } andis. r28,r27,0x00ff beq .LStrCopyWord { third byte zero? } andi. r28,r27,0xff00 beq .LStrCopy3Bytes { fourth byte zero? } andi. r28,r27,0x00ff { store next 4 bytes } stw r27,(r29) { increase dest address } addi r29,r29,4 beq .LStrCopyDone b .LStrCopyAligned { store left-overs } .LStrCopy3Bytes: sth r27,(r29) li r27,0 stb r27,2(r29) b .LStrCopyDone .LStrCopyWord: sth r27,(r29) b .LStrCopyDone .LStrCopyByte: stb r27,(r29) .LStrCopyDone: { r3 still contains dest here } end ['r4','r27','r28','r29','cr0','cr1']; function strecopy(dest,source : pchar) : pchar;assembler; { in: dest in r3, source in r4 } { out: result (end of new dest) in r3 } asm { empty/invalid string? } cmpli r3,0 { if yes, do nothing } beq .LStreCopyDone { clear two lowest bits of source address } rlwminm r28,r4,0,0,31-2 { get # of misaligned bytes } sub. r28,r28,r4 beq .LStreCopyAligned .LStreCopyAlignLoop: { decrease misaligned bytes counter (do it here already to improve } { jump prediction) } subic. r28,1 { load next byte } lbz r27,(r4) { end of string? } cmpli cr1,r27,0 { point to next source byte } addi r4,r4,1 { store byte } stb r27,(r3) { stop if end of string } beq cr1,.LStreCopyDone { point to next dest address } addi r3,r3,1 { loop if misaligned bytes left } bne .LStreCopyAlignLoop .balign 16 .LStreCopyAligned: { load next 4 bytes } lwz r27,(r4) { first/highest byte zero? (big endian!) } andis. r28,r27,0x0ff00 addi r4,r4,4 beq .LStreCopyByte { second byte zero? } andis. r28,r27,0x00ff beq .LStreCopyWord { third byte zero? } andi. r28,r27,0xff00 beq .LStreCopy3Bytes { fourth byte zero? } andi. r28,r27,0x00ff { store next 4 bytes } stw r27,(r3) { increase dest address } { the result must point to the terminating #0, so only add 3 } addi r3,r3,3 beq .LStreCopyDone { add another 1 for next char } addi r3,r3,1 b .LStreCopyAligned { store left-overs } .LStreCopy3Bytes: sth r27,(r3) li r27,0 stbu r27,2(r3) b .LStrCopyDone .LStreCopyWord: sth r27,(r3) addi r3,r3,1 b .LStrCopyDone .LStreCopyByte: stb r27,(r3) .LStreCopyDone: { r3 contains end of new string now } end ['r3','r4','r27','r28','cr0','cr1']; function strlcopy(dest,source : pchar;maxlen : longint) : pchar;assembler; asm { in: dest in r3, source in r4, maxlen in r5 } { out: result (dest) in r3 } asm { empty/invalid string? } cmpli r3,0 { if yes, do nothing } beq .LStrlCopyDone { maxlen in counter } mtctr r5 { clear two lowest bits of source address } rlwminm r28,r4,0,0,31-2 { get # of misaligned bytes } sub. r28,r28,r4 { since we have to return dest intact, use another register for } { dest in the copy loop } mr r29,r3 beq .LStrlCopyAligned .LStrlCopyAlignLoop: { if decreased maxlen counter = 0 (dz), stop } bdz .LStrlCopyByte { decrease misaligned bytes counter (do it here already to improve } { jump prediction) } subic. r28,1 { load next byte } lbz r27,(r4) { end of string? } cmpli cr1,r27,0 { point to next source byte } addi r4,r4,1 { store byte } stb r27,(r29) { point to next dest address } addi r29,r29,1 { stop if end of string } beq cr1,.LStrlCopyDone { loop while unaligned byte counter <> 0 } bne .LStrlCopyAlignLoop .balign 16 .LStrlCopyAligned: { load next 4 bytes } lwz r27,(r4) { first/highest byte zero? (big endian!) } andis. r28,r27,0x0ff00 addi r4,r4,4 { if decremented maxlen counter not zero (dnz) and no #0 (ne), } { continue (and hint that the most likely case is jump taken) } bdnzne+ .LNoStrlCopyByte b .LStrlCopyByte .LNoStrlCopyByte: { second byte zero? } andis. r28,r27,0x00ff bdnzne+ .LNoStrlCopyWord b .LStrlCopyWord .LNoStrlCopyWord: { third byte zero? } andi. r28,r27,0xff00 bdnzne+ .LNoStrlCopy3Bytes b .LStrlCopy3Bytes .LNoStrlCopy3Bytes: { fourth byte zero? } andi. r28,r27,0x00ff { store next 4 bytes } stw r27,(r29) { increase dest address } addi r29,r29,4 bdnzne .LStrlCopyAligned { replace last char with a #0 in case we stopped because the maxlen } { was reached } li r27,0 stb r27,-1(r29) b .LStrlCopyDone { store left-overs } .LStrlCopy3Bytes: { big endian! So move upper 16bits to lower 16bits} srwi r27,r27,16 sth r27,(r29) li r27,0 stb r27,2(r29) b .LStrlCopyDone .LStrlCopyWord: { clear lower 8 bits of low 16 bits } andi r27,r27,0x0ff00 sth r27,(r29) b .LStrlCopyDone .LStrlCopyByte: li r27,0 stb r27,(r29) .LStrlCopyDone: { r3 still contains dest here } end ['r4','r27','r28','r29','cr0','cr1','ctr']; function strlen(p : pchar) : longint;assembler; { in: p in r3 } { out: result (length) in r3 } { WARNING: if the used registers change here, also change strend!! (JM) } asm { empty/invalid string? } cmpli r3,0 { if yes, do nothing } beq .LStrLenNil { clear two lowest bits of source address } rlwminm r28,r3,0,0,31-2 { get # of misaligned bytes } sub. r28,r28,r3 { at the end, we substract r29 from r3 to get the length } mr r29,r3 beq .LStrLenAligned .LStrLenAlignLoop: { decrease misaligned bytes counter (do it here already to improve } { jump prediction) } subic. r28,1 { load next byte } lbz r27,(r3) { end of string? } cmpli cr1,r27,0 { stop if end of string } beq cr1,.LStrLenDone { point to next source byte } addi r3,r3,1 bne .LStrLenAlignLoop .balign 16 .LStrLenAligned: { load next 4 bytes } lwz r27,(r3) { first/highest byte zero? (big endian!) } andis. r28,r27,0x0ff00 beq .LStrLenDone { second byte zero? } andis. r28,r27,0x00ff { increase length } addi r3,r3,1 beq .LStrLenDone { third byte zero? } andi. r28,r27,0xff00 addi r3,r3,1 beq .LStrLenDone { fourth byte zero? } andi. r28,r27,0x00ff addi r3,r3,1 beq .LStrLenDone addi r3,r3,1 b .LStrLenAligned .LStrLenDone: sub r3,r29,r3 .LStrLenNil: end ['r3','r27','r28','r29','cr0','cr1']; function strend(p : pchar) : pchar;assembler; asm mr r26,r3 mflr r25 bl strlen mtlr r25 add r3,r26,r3 end ['r3','r25','r26','r27','r28','r29','cr0','cr1']; function strcomp(str1,str2 : pchar) : longint;assembler; { in: str1 in r3, str2 in r4 } { out: result (= 0 if strings equal, < 0 if str1 < str2, > 0 if str1 > str2 } { in r3 } asm { !!! } end; function strlcomp(str1,str2 : pchar;l : longint) : longint;assembler; { (same as strcomp, but maximally compare until l'th character) } { in: str1 in r3, str2 in r4, l in r5 } { out: result (= 0 if strings equal, < 0 if str1 < str2, > 0 if str1 > str2 } { in r3 } asm { !!! } end; function stricomp(str1,str2 : pchar) : longint;assembler; { in: str1 in r3, str2 in r4 } { out: result (= index of first differing character) in r3 } asm { !!! } end; function strlicomp(str1,str2 : pchar;l : longint) : longint;assembler; { (same as stricomp, but maximally compare until l'th character) } { in: str1 in r3, str2 in r4, l in r5 } { out: result (= index of first differing character) in r3 } asm { !!! } end; function strscan(p : pchar;c : char) : pchar;assembler; asm movl p,%eax xorl %ecx,%ecx testl %eax,%eax jz .LSTRSCAN // align movb c,%cl movl %eax,%esi andl $0xfffffff8,%eax movl $0xff,%edx movl p,%edi subl %eax,%esi jz .LSTRSCANLOOP xorl %eax,%eax .LSTRSCANALIGNLOOP: movb (%edi),%al // at .LSTRSCANFOUND, one is substracted from edi to calculate the position, // so add 1 here already (not after .LSTRSCAN, because then the test/jz and // cmp/je can't be paired) incl %edi testb %al,%al jz .LSTRSCAN cmpb %cl,%al je .LSTRSCANFOUND decl %esi jnz .LSTRSCANALIGNLOOP jmp .LSTRSCANLOOP .balign 16 .LSTRSCANLOOP: movl (%edi),%eax movl %eax,%esi // first char andl %edx,%eax // end of string -> stop jz .LSTRSCAN shrl $8,%esi cmpl %ecx,%eax movl %esi,%eax je .LSTRSCANFOUND1 // second char andl %edx,%eax jz .LSTRSCAN shrl $8,%esi cmpl %ecx,%eax movl %esi,%eax je .LSTRSCANFOUND2 // third char andl %edx,%eax jz .LSTRSCAN shrl $8,%esi cmpl %ecx,%eax movl %esi,%eax je .LSTRSCANFOUND3 // fourth char // all upper bits have already been cleared testl %eax,%eax jz .LSTRSCAN addl $4,%edi cmpl %ecx,%eax je .LSTRSCANFOUND jmp .LSTRSCANLOOP .LSTRSCANFOUND3: leal 2(%edi),%eax jmp .LSTRSCAN .LSTRSCANFOUND2: leal 1(%edi),%eax jmp .LSTRSCAN .LSTRSCANFOUND1: movl %edi,%eax jmp .LSTRSCAN .LSTRSCANFOUND: leal -1(%edi),%eax .LSTRSCAN: end ['EAX','ECX','ESI','EDI','EDX']; function strrscan(p : pchar;c : char) : pchar;assembler; asm xorl %eax,%eax movl p,%edi orl %edi,%edi jz .LSTRRSCAN movl $0xffffffff,%ecx cld xorb %al,%al repne scasb not %ecx movb c,%al movl p,%edi addl %ecx,%edi decl %edi std repne scasb cld movl $0,%eax jnz .LSTRRSCAN movl %edi,%eax incl %eax .LSTRRSCAN: end ['EAX','ECX','EDI']; function strupper(p : pchar) : pchar;assembler; asm movl p,%esi orl %esi,%esi jz .LStrUpperNil movl %esi,%edi .LSTRUPPER1: lodsb cmpb $97,%al jb .LSTRUPPER3 cmpb $122,%al ja .LSTRUPPER3 subb $0x20,%al .LSTRUPPER3: stosb orb %al,%al jnz .LSTRUPPER1 .LStrUpperNil: movl p,%eax end ['EAX','ESI','EDI']; function strlower(p : pchar) : pchar;assembler; asm movl p,%esi orl %esi,%esi jz .LStrLowerNil movl %esi,%edi .LSTRLOWER1: lodsb cmpb $65,%al jb .LSTRLOWER3 cmpb $90,%al ja .LSTRLOWER3 addb $0x20,%al .LSTRLOWER3: stosb orb %al,%al jnz .LSTRLOWER1 .LStrLowerNil: movl p,%eax end ['EAX','ESI','EDI']; { $Log$ Revision 1.1 2000-11-05 17:17:08 jonas + first implementation, not yet finished }