; ===== INTRODUCTION ===== ; This is supposed to provide some standard library functionality for stock symphony. ; In particular its supposed to work with an unmodified ISA, that means some choices are not ; optimal (RA being stored in flags for example) ; ===== ABI ===== ; ----- CALLING CONVENTION ----- ; n.a. zr ; preserved: sp, r8 - r12 ; scratch: flags, r1 - r7 ; arguments: r1 - r7 (r1 = 1st argument, r6 = 6th arg/stack args, r7 = 7th arg/stack res) ; result: r1, r2 (r1 = low word, r2 = high word) ; return address: r13 ; ----- STACK ----- ; grows downwards from top of memory ; arguments are passed in reverse order with the stack so: ; lowest address = 1st stack arg ; highest address = last stack arg ; ===== TYPES ===== pub include bit pub include imath ; int memcmp(uint8_t* a, uint8_t* b, size_t count); ; Compares two memory segment of equal length lexicographically. ; ; Arguments: ; - `r1`: A pointer to the first memory segment. ; - `r2`: A pointer to the second memory segment. ; - `r3`: The size of both memory segments. ; Results: ; - `r1`: ; - `0` if both segments are equal. ; - `<0` if the first segment is less than the second segment. ; - `>0` if the first segment is greater than the second segment. ; pub fn_memcmp: ; Exclusive end point of the first segment. add r3, r3, r1 sub r3, r3, 4 _memcmp__loop: load_32 r4, [r1] add r1, r1, 4 load_32 r5, [r2] add r2, r2, 4 ; Comparing two sequences of 4 bytes lexicographically is equivalent to ; comparing the corresponding big endian 32 bit words. cmp r4, r5 jne _memcmp__break ; Check if there are enough bytes left to continue with the vectorized loop. cmp r1, r3 jbe _memcmp__loop ; `r3 + 4 - r1 = = r3 - r1 mod 4` sub flags, r3, r1 ; Check if one of the lowest 2 bits is non-zero jbe _memcmp__rem ; If not, we are done. Both segments are equal. mov r1, 0 jmp r13 _memcmp__break: ; `flags` is the comparison result in the format of `cmp`. Convert it to the desired format. ; 00 => 0x40000000 > 0 ; 01 => 0x00000000 = 0 ; 10 => 0xC0000000 < 0 xor r1, flags, 1 lsl r1, r1, 30 jmp r13 _memcmp__rem: ; Compute `S = 8*(4 - )` and ; [r1] >> S, [r2] >> S mov r3, 8 load_32 r4, [r1] sub r3, r3, flags load_32 r5, [r2] lsl r3, r3, 3 lsr r4, r4, r3 lsr r5, r5, r3 ; Compare both values, now with garbage bytes removed. cmp r4, r5 jmp _memcmp__break ; void memcpy(void* src, void* dest, size_t count); ; Copies `count` bytes from `src` to `dest`. The two memory segments must not overlap. ; ; Arguments: ; - `r1`: Pointer to the memory segment to be copied. ; - `r2`: Pointer to the memory segment to be copied into. ; - `r3`: Byte size of both the `src` and `dest` segments. ; pub fn_memcpy: ; Exclusive end point of the source segment. add r3, r1, r3 ; Last index from where we can safely copy 8 bytes per loop iteration. sub r3, r3, 8 jmp _memcpy__loop_entry _memcpy__loop: ; Copy 8 bytes from `src` to `dest`. load_32 flags, [r1] add r1, r1, 4 store_32 [r2], flags add r2, r2, 4 load_32 flags, [r1] add r1, r1, 4 store_32 [r2], flags add r2, r2, 4 _memcpy__loop_entry: ; Check if we can process more data in the vectorized loop. cmp r1, r3 jbe _memcpy__loop ; The remaining amount of bytes `R` is `R = r3 + 8 - r1 = r3 - r1 mod 8`. sub flags, r3, r1 ; Test if `R` is not a multiple of `4`, i.e. the lowest 2 bits are non-zero. jbe _memcpy__rem ; `R` is a multiple of `4`. Special case this. ; Check if `R` is `0`, i.e. the third bit is also 0. In that case, we are already done. ; There are no conditional indirect jumps, so we can't return immediately. jge _memcpy__ret ; `R = 4`. No need to update `r1` or `r2`, we don't need them anymore. load_32 flags, [r1] store_32 [r2], flags _memcpy__ret: ; Return jmp r13 _memcpy__rem: ; Optimize the remaining cases for code size. ; End point of the source segment. add r3, r3, 8 ; We already handled the case `R = 0` earlier, ; so no bounds check needed for the first iteration. _memcpy__rem_loop: ; Copy 1 byte. load_8 flags, [r1] add r1, r1, 1 store_8 [r2], flags add r2, r2, 1 ; Check if we are still within the bounds. cmp r1, r3 jb _memcpy__rem_loop jmp r13 ; void memset32(uint8_t* dest, size_t count, uint32_t value); ; Fills `count` bytes in `dest` with `value`. If `count` is not a multiple of 4, ; the least significant bytes of `value` are cut off for the last entry. ; ; Arguments: ; - `r1`: A pointer to the destination segment. ; - `r2`: The size of the destination segment. ; - `r3`: The 32 bit value that the segment is filled with. ; pub fn_memset32: ; Exclusive end point of the destination segment. add r2, r1, r2 ; Last index from where we can safely write 8 bytes per loop iteration. sub r2, r2, 8 jmp _memset32__entry _memset32__loop: ; Set 8 bytes per loop iteraion. store_32 [r1], r3 add r1, r1, 4 store_32 [r1], r3 add r1, r1, 4 _memset32__entry: ; Check if we can process more data in the vectorized loop. cmp r1, r2 jbe _memset32__loop ; The remaining amount of bytes `R` is `R = r2 + 8 - r1 = r2 - r1 mod 8`. sub flags, r2, r1 ; Check if the third bit of the remainder is cleared. jge _memset32__r4 ; Otherwise set 4 bytes. store_32 [r1], r3 add r1, r1, 4 _memset32__r4: ; Check if the two least significant bits of the remainder are zero. ja _memset32__ret ; Handle the remaining bytes `R` individually, in reverse order. add r2, r2, 4 ; `r1 + 4 - r2 = 4 - R`. sub flags, r1, r2 ; Exclusive end point of the destination segment. add r2, r2, 4 ; Shift out the least significant `8*(4 - R)` bits of the value. lsl flags, flags, 3 lsr r3, r3, flags jmp _memset32__loop2_entry _memset32__loop2: sub r2, r2, 1 ; Write the least significant byte of the value... store_8 [r2], r3 ; and then shift it out. lsr r3, r3, 8 _memset32__loop2_entry: cmp r1, r2 jb _memset32__loop2 _memset32__ret: jmp r13