These illustrate an approach to initializing an address space suitable for running a C program that uses newlib. *** freestanding example apps: stand00: long-form %pcrel() assembler macros stand01: use pseudo-ops to implement crt0.S in an understandable manner stand02: - add some variables to the data and bss regions - now there is somehting to zero-out - note that the text region now is zero-padded in order to be able to initialize the page-aligned data region - see the bss region get zeroed out this time stand03: call main --> main() { return 0x8675309 } - the "call main" pseudo-operation is same as: cm1: auipc x1,%pcrel_hi(main) jalr x1,%pcrel_lo(callmain)(cm1) - how does 'call' know to use x1 as the return address register?????? Because the ABI says so! stand04: call main() and set the value of a variable in the BSS stand05: call main() and set the values from variables in the DATA and BSS regions. stand06: Create a char array in main() that is not used. Even with an initializer it is optimized away! stand07: (error) Create char array in main() and then use it. This examle fails due to missing memset()!! stand08: Add -lgcc and -lc libraries. stand09: (error) Add a call to malloc() that fails because there is no sbrk() stand10: (note that the text region is too big to hardcode the data to start at 0x1000) Add stubs for the newlib's stdlib dependancies in stub_stdlib.c Demonstrate that malloc() is working properly. Conclusion: We can now execute single-threaded C programs that can use functions from the standard library. There are no provisions, however, for things like I/O. *** Related issues: - use ABI names since x-this and x-that make thinking about arg-1 and arg-2 of a C func calls easier. Show reg table from RVALP here. - auipc + addi = how we put the address of some label into a register. This method is optimized to eliminate accessing memory (where we could just store a pointer). There is an 'la' pseudo-instruction: la a1,target ... that tells the assembler to do this: la1: auipc a1,%pcrel_hi(target) addi a1,a1,%pcrel_lo(la1) - pcrel_hi(label) represents an immediate-value suitable for auipc based on the label. This immediate will be the distance from the PC to the label!! - pcrel_lo(hi_label) is used to calc the right imm value for an addi insn matched to an auipc! - lui + addi = how we put an absolute value into a register There is an 'li' pseudo-instruction that can load any value into a register: li a1,0x12345678 The li pseudo-operation on an RV32I will expand into this: lui a1,%hi(0x12345678) addi a1,a1,%lo(0x12345678) On RV32 the assembler will only need to use either an addi or a lui and addi. Note that 'li' works on RV64 and RV128 machines too. To accomplish the loading the assembler will use a series of instructions like lui, addi, and shift.