mantle/alloc_handle_for_gtk [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
alloc_handle_for_gtk
SYNOPSIS
static uint64_t alloc_handle_for_gtk(void)
FUNCTION
Allocates a free handle to the GTK source ID mappings.
INPUTS
RESULT
Answers with a handle ID in the range 1..=MAX_GTK_SOURCES upon
success, or 0 if no more handles exist.
SEE ALSO
free_handle_for_gtk
SOURCE
{
for (int i = 0; i < MAX_GTK_SOURCES; i++) {
if (g_source_id_useds[i]) continue;
g_source_id_useds[i] = true;
return (uint64_t)i + 1;
}
return 0;
}
mantle/free_handle_for_gtk [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
free_handle_for_gtk
SYNOPSIS
static void free_handle_for_gtk(uint64_t h)
FUNCTION
Frees a previously allocated handle to the GTK source ID mappings. This function cannot fail.
INPUTS
RESULT
SEE ALSO
alloc_handle_for_gtk
SOURCE
{
int i = (int)h - 1;
if ((!h) || (h >= MAX_GTK_SOURCES)) return;
g_source_id_useds[i] = false;
}
mantle/globals [ Variables ]
[ Top ] [ mantle ] [ Variables ]
NAME
globals
FUNCTION
These globals basically emulates the process state of a program running inside Mantle.
MEMBERS
g_gui
Provides a link back to the emulator's application
state. This structure ultimately is used to provide
the RV64 emulator access to the memory map, from which
it fetches instructions and loads/stores from/to memory.
g_event_mask
The currently running application's event interest mask.
Bit 0 corresponds to mtInit, bit 1 to mtKeyDown, etc.
g_evproc
A pointer to the application's current event procedure.
This pointer is relative to the RISC-V's view of memory.
g_evsp
A pointer to the application's current stack bottom.
g_source_ids[]
A mapping of handle->GTK source IDs. Up to
MAX_GTK_SOURCES can be allocated at any given time.
Currently, that's 32, which ought to be enough for any
reasonable application I can think of.
GTK source IDs are often used for resources like
interval timers, et. al. that have to be routed through
the GTK main event loop for proper operation.
g_source_id_useds[]
A mapping of handle->source allocated flags.
If false, a handle remains unallocated and is free for
future use. If true, the handle refers to a GTK
source, and is not free for use.
BUGS
Some of these globals are GTK-specific, and irrelevant to, say,
a Win32 port. These should be refactored in a future release
to make porting easier.
mantle/mantle_run_until_next_event [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
mantle_run_until_next_event
SYNOPSIS
uint64_t mantle_run_until_next_event(gui_t* gui, uint64_t p0, uint64_t p2)
FUNCTION
Runs the user-mode event handler configured for the currently running program.
Part of running the event procedure is handling Mantle system calls. Thus, this function is also ultimately responsible for processing ECALLs from the user-mode RISC-V environment.
INPUTS
gui Pointer to the gui_t environment, through which the RISC-V emulator gains access to its memory map. p0 The event type the handler is requested to process. See also message_type_t. p2 The parameter unique to the current type of message. RESULT The RISC-V code runs as long as there is no unhandled trap or invokes the ecNextEvent ECALL. If the ecNextEvent ECALL is invoked, the result code provided in that call is returned. Otherwise, a trap-specific result is given instead (usually 0).
SEE ALSO
SOURCE
{
uint64_t p1 = g_get_monotonic_time();
rv64 rv;
uint64_t result = 0;
rv64_reset(&rv, g_evproc, g_evsp);
// rv64_reset() puts processor into machine-mode.
// Switch to user-mode.
rv.mode = PRIV_USER;
rv.status.mdt = rv.status.sdt = 0;
// Set parameters
rv64_xreg_at_put(&rv, A0, p0);
rv64_xreg_at_put(&rv, A1, p1);
rv64_xreg_at_put(&rv, A2, p2);
bool next_event = false;
do {
rv64_run_until_trap(&rv, &gui->slots);
// We don't handle interrupt requests yet.
// Ignore it.
if (rv.mcause.interrupt) break;
// We know it's a synchronous trap at this point.
switch (rv.mcause.reason.e) {
case U_ECALL:
uint64_t a0 = rv64_xreg_at(&rv, A0);
uint64_t a1 = rv64_xreg_at(&rv, A1);
uint64_t a2 = rv64_xreg_at(&rv, A2);
uint64_t a7 = rv64_xreg_at(&rv, A7);
rv.mepc += 4; // skip over ecall insn
switch (a7) {
case ecNextEvent:
result = a0;
next_event++;
break;
case ecGetScreenConfig:
rv64_xreg_at_put(&rv, A0, gui->screen_width);
rv64_xreg_at_put(&rv, A1, gui->screen_height);
rv64_xreg_at_put(&rv, A2, (uint64_t)((int32_t)gui->screen_base));
rv64_return_from_trap(&rv, rv.mode);
break;
case ecDumpRegs:
rose_dump_registers(&rv);
rv64_return_from_trap(&rv, rv.mode);
break;
case ecSetEventsDesired:
rv64_xreg_at_put(&rv, A0, rose_set_events_desired(a0));
rv64_return_from_trap(&rv, rv.mode);
break;
case ecSetEvProcPC:
rv64_xreg_at_put(&rv, A0, rose_set_event_proc_pc(a0));
rv64_return_from_trap(&rv, rv.mode);
break;
case ecSetEvProcSP:
rv64_xreg_at_put(&rv, A0, rose_set_event_proc_sp(a0));
rv64_return_from_trap(&rv, rv.mode);
break;
case ecBeginPaint:
// For now, does nothing. Must always succeed.
rv64_return_from_trap(&rv, rv.mode);
break;
case ecEndPaint:
gui_refresh_screen(gui);
rv64_return_from_trap(&rv, rv.mode);
break;
// These interfaces slightly leaks the Virtual Mantle
// abstraction by directly exposing GTK/GLib timers.
case ecStartTimer:
guint period_ms = (guint)(rv64_xreg_at(&rv, A0));
{
uint64_t handle = alloc_handle_for_gtk();
guint timer_id = g_source_ids[handle] = g_timeout_add(period_ms, (GSourceFunc)rose_event_timer_tick, (void*)handle);
if (!timer_id) {
free_handle_for_gtk(handle);
handle = 0;
}
rv64_xreg_at_put(&rv, A0, handle);
}
rv64_return_from_trap(&rv, rv.mode);
break;
case ecStopTimer:
{
uint64_t handle = rv64_xreg_at(&rv, A0);
if (handle && (handle <= MAX_GTK_SOURCES)) {
guint timer_id = g_source_ids[handle];
g_source_remove(timer_id);
free_handle_for_gtk(handle);
}
}
rv64_xreg_at_put(&rv, A0, 1);
rv64_return_from_trap(&rv, rv.mode);
break;
case ecQuitVM:
gui_terminate_emulator(rv64_xreg_at(&rv, A0));
rv64_return_from_trap(&rv, rv.mode);
break;
default:
// handle service call here, then return to RISC-V code.
rv64_return_from_trap(&rv, rv.mode);
break;
}
break;
default:
fprintf(stderr, "UNHANDLED EXCEPTION:\n");
fprintf(stderr, "mcause.reason=%d mepc=$%016lX\n", rv.mcause.reason.e, rv.mepc);
rose_dump_registers(&rv);
next_event++; // break from run loop
break;
}
} while(!next_event);
return result;
}
mantle/rose_dump_registers [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
rose_dump_registers
SYNOPSIS
void rose_dump_registers(rv64* rv)
FUNCTION
Dumps the user-mode state to a separate debugging channel, which is core-dependent.
INPUTS
rv Pointer to the RISC-V run-time state. RESULT
SEE ALSO
SOURCE
{
fprintf(stderr,
"GP=$%016lX RA=$%016lX SP=$%016lX\n",
rv64_xreg_at(rv, GP), rv64_xreg_at(rv, RA), rv64_xreg_at(rv, SP));
fprintf(stderr,
"A0=$%016lX A1=$%016lX A2=$%016lX A3=$%016lX\n",
rv64_xreg_at(rv, A0), rv64_xreg_at(rv, A1), rv64_xreg_at(rv, A2), rv64_xreg_at(rv, A3));
fprintf(stderr,
"A4=$%016lX A5=$%016lX A6=$%016lX A7=$%016lX\n",
rv64_xreg_at(rv, A4), rv64_xreg_at(rv, A5), rv64_xreg_at(rv, A6), rv64_xreg_at(rv, A7));
fprintf(stderr,
"S0=$%016lX S1=$%016lX S2=$%016lX S3=$%016lX\n",
rv64_xreg_at(rv, S0), rv64_xreg_at(rv, S1), rv64_xreg_at(rv, S2), rv64_xreg_at(rv, S3));
fprintf(stderr,
"S4=$%016lX S5=$%016lX S6=$%016lX S7=$%016lX\n",
rv64_xreg_at(rv, S4), rv64_xreg_at(rv, S5), rv64_xreg_at(rv, S6), rv64_xreg_at(rv, S7));
fprintf(stderr,
"T0=$%016lX T1=$%016lX T2=$%016lX T3=$%016lX\n",
rv64_xreg_at(rv, T0), rv64_xreg_at(rv, T1), rv64_xreg_at(rv, T2), rv64_xreg_at(rv, T3));
fprintf(stderr,
"T4=$%016lX T5=$%016lX T6=$%016lX\n",
rv64_xreg_at(rv, T4), rv64_xreg_at(rv, T5), rv64_xreg_at(rv, T6));
fprintf(stderr, "----------------------------------------------------------------\n");
}
mantle/rose_event_key_down, mantle/rose_event_key_up [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
rose_event_key_down
rose_event_key_up
SYNOPSIS
* bool rose_event_key_down(gui_t* gui, uint32_t keycode) * bool rose_event_key_up(gui_t* gui, uint32_t keycode)
FUNCTION
If the currently running application has the mtKeyDown or mtKeyUp flag set in their event desired flags, then dispatch the keyboard event to the current event procedure, and return the result of that procedure..
Otherwise, assume no handler exists, and return false on the application's behalf.
INPUTS
gui A pointer to the gui_t, through which the RISC-V emulator can gain access to its memory map. keycode A Gdk-compatible key code corresponding to the key that was pressed or released. RESULT Whatever the evproc returns in A0, or false if the application isn't interested.
SEE ALSO
mantle/rose_event_timer_tick [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
rose_event_timer_tick
SYNOPSIS
bool rose_event_timer_tick(gpointer timer_id_as_ptr)
FUNCTION
If the currently running application has the mtKeyDown or mtKeyUp flag set in their event desired flags, then dispatch the keyboard event to the current event procedure, and return the result of that procedure.
Otherwise, assume no handler exists, and return false on the application's behalf.
INPUTS
gui A pointer to the gui_t, through which the RISC-V emulator can gain access to its memory map. keycode A Gdk-compatible key code corresponding to the key that was pressed or released. RESULT Whatever the evproc returns in A0, or false if the application isn't interested.
SEE ALSO
SOURCE
{
uint64_t handle = (uint64_t)timer_id_as_ptr;
bool result = false;
if (g_event_mask & MASK(mtTimerTick)) {
result = mantle_run_until_next_event(g_gui, mtTimerTick, handle);
}
return result;
}
mantle/rose_set_event_proc_pc, mantle/rose_set_event_proc_sp [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
rose_set_event_proc_pc
rose_set_event_proc_sp
SYNOPSIS
* uint64_t rose_set_event_proc_pc(uint64_t pc) * uint64_t rose_set_event_proc_sp(uint64_t sp)
FUNCTION
These functions retrieves the current value of the stack pointer or event procedure address, then sets their respective new values.
NOTE Both pc and sp MUST be 4-byte aligned, or you'll get address alignment traps the moment an event procedure tries to run.
INPUTS
pc The address of the new event procedure.
sp The stack pointer to use when calling the event procedure.
RESULT
Depending on the function you call, either the previous event
handler procedure address or the corresponding stack pointer
is returned.
SEE ALSO
SOURCE
uint64_t
rose_set_event_proc_pc(uint64_t pc)
{
uint64_t old_pc = g_evproc;
g_evproc = pc;
return old_pc;
}
uint64_t
rose_set_event_proc_sp(uint64_t sp)
{
uint64_t old_sp = g_evsp;
g_evsp = sp;
return old_sp;
}
mantle/rose_set_events_desired [ Functions ]
[ Top ] [ mantle ] [ Functions ]
NAME
rose_set_events_desired
SYNOPSIS
uint64_t rose_set_events_desired(uint64_t event_mask)
FUNCTION
Retrieves the current set of events desired flag bits, then sets the events desired flag bits to the new value.
INPUTS
event_mask The new set of events desired by the application.
RESULT
Answers with the previous value of the events desired mask.
SEE ALSO
SOURCE
{
uint64_t old_event_mask = g_event_mask;
g_event_mask = event_mask;
return old_event_mask;
}