Description
First trial of refactoring, the wasm branch's latest commit is the result.
Since state_t
is a user-provided data, so all runtime defined value(often change) shall be stored there. For instance, the emulated target program's argc and argv, and the emulator's parameter. The following have been adjusted to reflect the changes:
-
state_t *state_new(void)
----->state_t *state_new(uint32_t mem_size, int argc, char **argv, bool allow_misalign, bool quiet_output)
mem_size
is used formemory_new
because different runtimes may have memory requirements (for example, the page size in WebAssembly is 64KiB), the defaultMEM_SIZE
(2^32 - 1) is not appropriate for that. The rest of parameters are runtime defined value. -
riscv_t *rv_create(const riscv_io_t *io, riscv_user_t userdata, int argc, char **args, bool output_exit_code)
----->riscv_t *rv_create(riscv_user_t userdata)
Much cleaner function signature. -
void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args)
----->void rv_reset(riscv_t *rv, riscv_word_t pc)
We can userv->userdata
to get the requiredargc
andargv
. -
Since memory I/O handlers are rarely changed, it makes less sense to define them during runtime (makes porting difficult). Instead, I believe it is preferable to link them during build time. If really want to change the implementations, make a new C file and link it during build time might be a better choice.
To do this, some changes are made:- define memory I/O handlers in
rv_create
and link during build time - to make memory write interfaces match to compatible with the function pointers,
MEM_WRITE_IMPL
macro has to be changed: -
src/io.c
- define memory I/O handlers in
// old
#define MEM_WRITE_IMPL(size, type) \
void memory_write_##size(uint32_t addr, const uint8_t *src) \
{ \
*(type *) (data_memory_base + addr) = *(const type *) src; \
}
// new
#define MEM_WRITE_IMPL(size, type) \
void memory_write_##size(uint32_t addr, const type src) \
{ \
*(type *) (data_memory_base + addr) = src; \
}
- the calling of
memory_write_w
in "src/syscall.c" shall be changed accordingly: -
src/syscall.c
// old
memory_write_w(tv + 0, (const uint8_t *) &tv_s.tv_sec);
// new
memory_write_w(tv + 0, *((const uint32_t *) &tv_s.tv_sec));
For notably change, the "pre.js" of the wasm branch do not define IO on its own anymore compare to first attempt.(more abstraction)
-
Change all
uint32_t
anduint16_t
anduint8_t
in riscv.[ch] toriscv_word_t
andriscv_half_t
andriscv_byte_t
in function signature respectively for consistency. -
bool elf_load(elf_t *e, riscv_t *rv, memory_t *mem);
----->bool elf_load(elf_t *e, riscv_t *rv);
The memory instance required byelf_load
can be accessed viarv
's userdata.
I am wondering shall we abstract the FILE
defined in state_new
as a parameter of state_new
. Without abstraction, the emulator always depends on standard io(e.g., stdin
, stdout
, stderr
). What if the user want to use a file instead of stdout
?
Related to: #75