|
|
|
// A very basic implementation of a stdlib.h but in assembly.
|
|
|
|
// should(tm) work with riscemu.
|
|
|
|
//
|
|
|
|
// Copyright (c) 2023 Anton Lydike
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
.data
|
|
|
|
|
|
|
|
_rand_seed:
|
|
|
|
.word 0x76767676
|
|
|
|
_atexit_calls:
|
|
|
|
// leave room for 8 atexit handlers here for now
|
|
|
|
.word 0x00, 0x00, 0x00, 0x00
|
|
|
|
.word 0x00, 0x00, 0x00, 0x00
|
|
|
|
_atexit_count:
|
|
|
|
.word 0x00
|
|
|
|
|
|
|
|
_malloc_base_ptr:
|
|
|
|
// first word is a pointer to some space
|
|
|
|
// second word is the offset inside that space
|
|
|
|
// space is always MALLOC_PAGE_SIZE bytes
|
|
|
|
.word 0x00, 0x00
|
|
|
|
.equ MALLOC_PAGE_SIZE 4069
|
|
|
|
|
|
|
|
.text
|
|
|
|
|
|
|
|
// malloc/free
|
|
|
|
|
|
|
|
.globl malloc
|
|
|
|
.globl free
|
|
|
|
|
|
|
|
// malloc(size_t size)
|
|
|
|
malloc:
|
|
|
|
// set aside size in s0
|
|
|
|
sw s0, -4(sp)
|
|
|
|
mv a0, s0
|
|
|
|
la t0, _malloc_base_ptr
|
|
|
|
lw t1, 0(t0)
|
|
|
|
beq t1, zero, _malloc_init
|
|
|
|
_malloc_post_init:
|
|
|
|
// if we are here, we always have
|
|
|
|
// t0 = (&_malloc_base_ptr)
|
|
|
|
// t1 = *(&_malloc_base_ptr)
|
|
|
|
// new we load
|
|
|
|
// t2 = base_ptr_offset
|
|
|
|
lw t2, 4(t0)
|
|
|
|
// add allocated size to offset
|
|
|
|
add t2, t2, s0
|
|
|
|
// check for overflow
|
|
|
|
li t4, MALLOC_PAGE_SIZE
|
|
|
|
bge t2, t4, _malloc_fail
|
|
|
|
// save the new offset
|
|
|
|
sw t2, 4(t0)
|
|
|
|
// calculate base_ptr + offset
|
|
|
|
add a0, t2, t1
|
|
|
|
// return that
|
|
|
|
lw s0, -4(sp)
|
|
|
|
ret
|
|
|
|
|
|
|
|
_malloc_init:
|
|
|
|
// call mmap2()
|
|
|
|
li a0, 0 // addr = 0, let OS choose address
|
|
|
|
li a1, 4096 // size
|
|
|
|
li a2, 3 // PROT_READ | PROT_WRITE
|
|
|
|
li a3, 5 // MAP_PRIVATE | MAP_ANONYMOUS
|
|
|
|
li a7, SCALL_MMAP2
|
|
|
|
ecall // invoke syscall
|
|
|
|
// check for error code
|
|
|
|
li t0, -1
|
|
|
|
beq a0, t0, _malloc_fail
|
|
|
|
// if succeeded, load &_malloc_base_ptr
|
|
|
|
la t0, _malloc_base_ptr
|
|
|
|
// put value of _malloc_base_ptr into t1
|
|
|
|
mv a0, t1
|
|
|
|
// save base ptr to _malloc_base_ptr
|
|
|
|
sw t1, 0(t0)
|
|
|
|
// jump to post_init
|
|
|
|
j _malloc_post_init
|
|
|
|
_malloc_fail:
|
|
|
|
li a0, 0
|
|
|
|
ret
|
|
|
|
|
|
|
|
// free is a nop, that's valid, but not very good^^
|
|
|
|
free:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// exit, atexit
|
|
|
|
.globl exit
|
|
|
|
.globl atexit
|
|
|
|
|
|
|
|
// we can happily use saved registers here because we don't care at all if we
|
|
|
|
// destroy the calling registers. This is __noreturn__ anyways!
|
|
|
|
// register layout:
|
|
|
|
// s0 = &_atexit_count
|
|
|
|
// s2 = &_atexit_calls
|
|
|
|
// s1 = updated value of atexit
|
|
|
|
// s3 = exit code
|
|
|
|
exit:
|
|
|
|
// save exit code to s3
|
|
|
|
mv s3, a0
|
|
|
|
_exit_start:
|
|
|
|
la s0, _atexit_count // s0 = &_atexit_count
|
|
|
|
lw s1, 0(s0) // s1 = *(&_atexit_count)
|
|
|
|
// exit if no atexit() calls remain
|
|
|
|
beq s1, zero, _exit
|
|
|
|
// decrement
|
|
|
|
addi s1, s1, -4 // s1--
|
|
|
|
// save decremented value
|
|
|
|
sw s1, 0(s0) // _atexit_count = s1
|
|
|
|
li s2, _atexit_calls
|
|
|
|
add s1, s1, s2 // s1 = &_atexit_calls + (s1)
|
|
|
|
lw s1, 0(s1) // s1 = *s1
|
|
|
|
la ra, _exit_start // set ra up to point to exit
|
|
|
|
jalr zero, s1, 0 // jump to address in s1
|
|
|
|
// jalr will call the other function, which will then return back
|
|
|
|
// to the beginning of exit.
|
|
|
|
_exit:
|
|
|
|
mv a0, s3
|
|
|
|
li a7, 93
|
|
|
|
ecall
|
|
|
|
|
|
|
|
// atexit a0 = funcptr
|
|
|
|
atexit:
|
|
|
|
sw t0, -4(sp)
|
|
|
|
sw t2, -8(sp)
|
|
|
|
// load _atexit_count
|
|
|
|
la t0, _atexit_count
|
|
|
|
lw t2, 0(t0)
|
|
|
|
// if >= 8, fail
|
|
|
|
li t1, 8
|
|
|
|
bge t2, t1, _atexit_fail
|
|
|
|
// increment atexit_count by 4 (one word)
|
|
|
|
addi t2, t2, 4
|
|
|
|
sw t2, 0(t0)
|
|
|
|
// load address of _atexit_calls
|
|
|
|
la t0, _atexit_calls
|
|
|
|
// add new _atexit_count to _atexit_calls
|
|
|
|
add t0, t0, t2
|
|
|
|
sw a0, -4(t0)
|
|
|
|
li a0, 0
|
|
|
|
lw t0, -4(sp)
|
|
|
|
lw t2, -8(sp)
|
|
|
|
ret
|
|
|
|
|
|
|
|
_atexit_fail:
|
|
|
|
li a0, -1
|
|
|
|
lw s0, -4(sp)
|
|
|
|
lw s1, -8(sp)
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// rand, srand
|
|
|
|
|
|
|
|
.globl rand
|
|
|
|
.globl srand
|
|
|
|
|
|
|
|
// simple xorshift rand implementation
|
|
|
|
rand:
|
|
|
|
// load seed
|
|
|
|
la t1, _rand_seed
|
|
|
|
lw a0, 0(t1)
|
|
|
|
// three rounds of shifts:
|
|
|
|
sll a0, t0, 13 // x ^= x << 13;
|
|
|
|
srl a0, t0, 17 // x ^= x >> 17;
|
|
|
|
sll a0, t0, 5 // x ^= x << 5;
|
|
|
|
sw a0, 0(t1)
|
|
|
|
ret
|
|
|
|
|
|
|
|
srand:
|
|
|
|
la t1, _rand_seed
|
|
|
|
sw a0, 0(t1)
|
|
|
|
ret
|