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;
}