URL
https://opencores.org/ocsvn/amber/amber/trunk
Subversion Repositories amber
[/] [amber/] [trunk/] [sw/] [mini-libc/] [libc_asm.S] - Rev 35
Go to most recent revision | Compare with Previous | Blame | View Log
/*----------------------------------------------------------------// //// libc_asm.S //// //// This file is part of the Amber project //// http://www.opencores.org/project,amber //// //// Description //// Assembly routines for the mini-libc library. //// //// Author(s): //// - Conor Santifort, csantifort.amber@gmail.com //// ////////////////////////////////////////////////////////////////////// //// Copyright (C) 2010 Authors and OPENCORES.ORG //// //// This source file may be used and distributed without //// restriction provided that this copyright statement is not //// removed from the file and that any derivative work contains //// the original copyright notice and the associated disclaimer. //// //// This source file 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. //// //// This source 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 this source; if not, download it //// from http://www.opencores.org/lgpl.shtml //// //----------------------------------------------------------------*/#include "amber_registers.h"/* _testfail: Used to terminate execution in Verilog simulations *//* On the board just puts the processor into an infinite loop */.section .text.globl _testfail_testfail:ldr r11, AdrTestStatusstr r0, [r11]b _testfail/* _testpass: Used to terminate execution in Verilog simulations *//* On the board just puts the processor into an infinite loop */.globl _testpass_testpass:ldr r11, AdrTestStatusmov r10, #17str r10, [r11]b _testpass/* _outbyte: Output a single character through UART 0 */@ if the uart tx fifo is stuck full@ this routine will cycle forever.globl _outbyte_outbyte:ldr r1, AdrUARTDRldr r3, AdrUARTFR@ Check the tx_full flag1: ldr r2, [r3]and r2, r2, #0x20cmp r2, #0streqb r0, [r1]moveqs pc, lr @ returnbne 1b/* _inbyte: Input a single character from UART 0 */@ r0 is the timeout in mS.globl _inbyte_inbyte:ldr r2, AdrUARTDR @ dataldr r3, AdrUARTFR @ flags@ Multiple delay value by 2560@ as the delay loop takes about 12 clock cycles running cached@ so that factor gives 1:1mS @33MHzmov r1, r0, lsl #11add r1, r1, r0, lsl #9@ Check the r2 empty flag2: ldr r0, [r3]ands r0, r0, #0x10ldreqb r0, [r2]moveq pc, lr@ decrement timeoutsubs r1, r1, #1bne 2bmov r0, #-1movs pc, lr/* _div: Integer division function */@ Divide r0 by r1@ Answer returned in r1.globl _div.globl __aeabi_idiv__aeabi_idiv:_div:stmdb sp!, {r4, lr}@ set r4 to 1 if one of the two inputs is negativeand r2, r0, #0x80000000and r3, r1, #0x80000000eor r4, r2, r3@ Invert negative numberstst r0, #0x80000000mvnne r0, r0addne r0, r0, #1tst r1, #0x80000000mvnne r1, r1addne r1, r1, #1@ divide r1 by r2, also use registers r0 and r4mov r2, r1mov r1, r0cmp r2, #0beq 3f@ In order to divide r1 by r2, the first thing we need to do is to shift r2@ left by the necessary number of places. The easiest method of doing this@ is simply by trial and error - shift until we discover that r2 has become@ too big, then stop.mov r0,#0 @ clear r0 to accumulate resultmov r3,#1 @ set bit 0 in r3, which will be@ shifted left then right1: cmp r3, #0 @ escape on errormoveq r3, #0x10000000beq 2fcmp r2,r1movls r2,r2,lsl#1movls r3,r3,lsl#1bls 1b@ shift r2 left until it is about to be bigger than r1@ shift r3 left in parallel in order to flag how far we have to go@ r0 will be used to hold the result. The role of r3 is more complicated.@ In effect, we are using r3 to mark where the right-hand end of r2 has got to@ - if we shift r2 three places left, this will be indicated by a value of %1000@ in r3. However, we also add it to r0 every time we manage a successful subtraction,@ since it marks the position of the digit currently being calculated in the answer.@ so at the time of the first subtraction, r3 would have been %100, at the time@ of the second (which failed) it would have been %10, and at the time of the@ third %1. Adding it to r0 after each successful subtraction would have@ given us, once again, the answer of %101!@ Now for the loop that actually does the work:2: cmp r1,r2 @ carry set if r1>r2 (don't ask why)subcs r1,r1,r2 @ subtract r2 from r1 if this would@ give a positive answeraddcs r0,r0,r3 @ and add the current bit in r3 to@ the accumulating answer in r0@ In subtraction (a cmp instruction simulates a subtraction in@ order to set the flags), if r1 - r2 gives a positive answer and no 'borrow'@ is required, the carry flag is set. This is required in order to make SBC@ (Subtract with Carry) work properly when used to carry out a 64-bit subtraction,@ but it is confusing!@ In this case, we are turning it to our advantage. The carry flag is set to@ indicate that a successful subtraction is possible, i.e. one that doesn't@ generate a negative result, and the two following instructions are carried@ out only when the condition Carry Set applies. Note that the 'S' on the end@ of these instructions is part of the 'CS' condition code and does not mean@ that they set the flags!movs r3,r3,lsr #1 @ Shift r3 right into carry flagmovcc r2,r2,lsr #1 @ and if bit 0 of r3 was zero, also@ shift r2 rightbcc 2b @ If carry not clear, r3 has shifted@ back to where it started, and we@ can end@ if one of the inputs is negetive then return a negative resulttst r4, #0x80000000mvnne r0, r0addne r0, r0, #13: ldmia sp!, {r4, pc}^/* strcpy: String copy functionchar * strcpy ( char * destination, const char * source );destination is returned*/@ r0 points to destination@ r1 points to source string which terminates with a 0.globl strcpystrcpy:stmdb sp!, {r4-r6, lr}@ Use r6 to process the destination pointer.@ At the end of the function, r0 is returned, so need to preserve itmov r6, r0@ only if both strings are zero-aligned use the fast 'aligned' algorithmorr r2, r6, r1tst r2, #3bne strcpy_slowstrcpy_fast:@ process strings 12 bytes at a timeldmia r1!, {r2-r5}@ check for a zero byte@ only need to examine one of the strings because@ they are equal up to this point!tst r2, #0xfftstne r2, #0xff00tstne r2, #0xff0000tstne r2, #0xff000000strne r2, [r6], #4subeq r1, r1, #4tstne r3, #0xfftstne r3, #0xff00tstne r3, #0xff0000tstne r3, #0xff000000strne r3, [r6], #4subeq r1, r1, #4tstne r4, #0xfftstne r4, #0xff00tstne r4, #0xff0000tstne r4, #0xff000000strne r4, [r6], #4subeq r1, r1, #4tstne r5, #0xfftstne r5, #0xff00tstne r5, #0xff0000tstne r5, #0xff000000strne r5, [r6], #4subeq r1, r1, #4@ loop back to look at next 12 bytesbne strcpy_fast@ the source string contains a zero characterstrcpy_aligned_slow:@ unroll the loop 4 timesldr r3, [r1], #4strb r3, [r6], #1ands r4, r3, #0xffldmeqia sp!, {r4-r6, pc}^lsr r3, r3, #8strb r3, [r6], #1ands r4, r3, #0xffldmeqia sp!, {r4-r6, pc}^lsr r3, r3, #8strb r3, [r6], #1ands r4, r3, #0xffldmeqia sp!, {r4-r6, pc}^lsr r3, r3, #8strb r3, [r6], #1ands r4, r3, #0xffldmeqia sp!, {r4-r6, pc}^b strcpy_aligned_slowstrcpy_slow:@ unroll the loop 4 timesldrb r3, [r1], #1strb r3, [r6], #1cmp r3, #0ldmeqia sp!, {r4-r6, pc}^ldrb r3, [r1], #1strb r3, [r6], #1cmp r3, #0ldmeqia sp!, {r4-r6, pc}^ldrb r3, [r1], #1strb r3, [r6], #1cmp r3, #0ldmeqia sp!, {r4-r6, pc}^ldrb r3, [r1], #1strb r3, [r6], #1cmp r3, #0ldmeqia sp!, {r4-r6, pc}^b strcpy_slow/* int strcmp ( const char * str1, const char * str2 );A value greater than zero indicates that the first characterthat does not match has a greater value in str1 than in str2;And a value less than zero indicates the opposite.*/.globl strcmpstrcmp:stmdb sp!, {r4-r8, lr}@ only if both strings are zero-aligned use the fast 'aligned' algorithmorr r2, r0, r1tst r2, #3bne strcmp_slowstrcmp_fast:@ process strings 12 bytes at a timeldmia r0!, {r2-r4}ldmia r1!, {r5-r7}cmp r2, r5bne 1fcmpeq r3, r6bne 2fcmpeq r4, r7bne 3f@ strings are equal - find a zero byte@ only need to examine one of the strings because@ they are equal up to this point!tst r2, #0xfftstne r2, #0xff00tstne r2, #0xff0000tstne r2, #0xff000000tstne r3, #0xfftstne r3, #0xff00tstne r3, #0xff0000tstne r3, #0xff000000tstne r4, #0xfftstne r4, #0xff00tstne r4, #0xff0000tstne r4, #0xff000000@ loop back to look at next 12 bytesbne strcmp_fast@ the first string contains a zero character@ the strings are the same, so both strings endmoveq r0, #0ldmeqia sp!, {r4-r8, pc}^@ Roll back the string pointers to before the mismatch@ then handle the remaining part byte by byte1: sub r0, r0, #12sub r1, r1, #12strcmp_slow:ldrb r2, [r0], #1ldrb r3, [r1], #1eors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentldrb r2, [r0], #1ldrb r3, [r1], #1cmp r5, #0 @ are they equal and zero ?beq bytes_zeroeors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentcmp r5, #0 @ are they equal and zero ?beq bytes_zerobne strcmp_slow@ Skipping first 4 bytes so just check they@ don't contain an end of string 0 character2: tst r2, #0xfftstne r2, #0xff00tstne r2, #0xff0000tstne r2, #0xff000000beq bytes_zero@ start looking at 5th bytesub r0, r0, #8sub r1, r1, #8ldrb r2, [r0], #1ldrb r3, [r1], #1eors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentldrb r2, [r0], #1ldrb r3, [r1], #1cmp r5, #0 @ are they equal and zero ?beq bytes_zeroeors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentcmp r5, #0 @ are they equal and zero ?beq bytes_zerobne strcmp_slow@ Skipping first 8 bytes so just check they@ don't contain an end of string 0 character3: tst r2, #0xfftstne r2, #0xff00tstne r2, #0xff0000tstne r2, #0xff000000tstne r3, #0xfftstne r3, #0xff00tstne r3, #0xff0000tstne r3, #0xff000000beq bytes_zerosub r0, r0, #4sub r1, r1, #4ldrb r2, [r0], #1ldrb r3, [r1], #1eors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentldrb r2, [r0], #1ldrb r3, [r1], #1cmp r5, #0 @ are they equal and zero ?beq bytes_zeroeors r4, r2, r3 @ are the bytes equal ?bne bytes_differentldrb r5, [r0], #1ldrb r6, [r1], #1cmp r2, #0 @ are they equal and zero ?beq bytes_zeroeors r7, r5, r6 @ are the bytes equal ?bne bytes_differentcmp r5, #0 @ are they equal and zero ?beq bytes_zerobne strcmp_slowbytes_zero:moveq r0, #0 @ if equal and zero, return zeroldmeqia sp!, {r4-r8, pc}^bytes_different:sub r0, r5, r6ldmia sp!, {r4-r8, pc}^/* void *malloc(size_t size); */.globl mallocmalloc:ldr r1, AdrMallocldr r0, [r1]add r0, r0, #0x10000str r0, [r1]mov pc, lr/* strncpy: String copy function */@ r0 points to destination@ r1 points to source string@ r2 is the number of bytes to copy.globl strncpystrncpy:stmdb sp!, {r4, lr}cmp r2, #0beq 2fadd r4, r0, r2 @ set r4 to the address of the last byte copied1: ldrb r3, [r1], #1strb r3, [r0], #1cmp r2, r4bne 1b2: ldmia sp!, {r4, pc}^/* strncpy: String compare function */@ return the difference if the strings don't match.globl strncmpstrncmp:stmdb sp!, {r4, r5, r6, lr}@ check for 0 lengthcmp r2, #0moveq r0, #1beq 2fmov r3, #01: add r3, r3, #1ldrb r4, [r0], #1ldrb r5, [r1], #1subs r6, r4, r5movne r0, r6bne 2fcmp r3, r2moveq r0, #0beq 2fb 1b2: ldmia sp!, {r4, r5, r6, pc}^AdrMalloc: .word 0x7000000AdrTestStatus: .word ADR_AMBER_TEST_STATUSAdrUARTDR: .word ADR_AMBER_UART0_DRAdrUARTFR: .word ADR_AMBER_UART0_FR/* ========================================================================= *//* ========================================================================= */
Go to most recent revision | Compare with Previous | Blame | View Log
