Skip to content

Commit 4f4b29a

Browse files
committed
Apply bidirectional queue design to the input system
This commit splits the input system into two parts, event and submission, and introduces two system calls, setup_queue and submit, the former is used to assist in the creation of queues, which is part of the communication bridge between the user application code and the emulator, and the latter is used to notify the emulator that a submission (or a command) should be processed. The design of these system calls is heavily based on Linux io_uring interface.
1 parent 09f4e65 commit 4f4b29a

File tree

3 files changed

+106
-61
lines changed

3 files changed

+106
-61
lines changed

docs/syscall.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,32 @@ These system calls are solely for the convenience of accessing the [SDL library]
2727

2828
If a window does not already exist, one will be created with the specified `width` and `height`. The `screen` buffer will replace the content of the framebuffer, passing a different `width` or `height` compared to the size of the window is undefined behavior. This system call additionally polls events from the SDL library, and, if necessary, update the internal input specific event queue.
2929

30-
### `poll_event(event)` - Poll a input specific event from SDL
30+
### `setup_queue` - Setup input system's dedicated event and submission queue
3131

3232
**system call number**: `0xC0DE`
3333

34-
**synopsis**: `int poll_event(void *event)`
34+
**synopsis**: `void *setup_queue(int capacity, unsigned int* event_count)`
3535

36-
`event` will be filled with the polled event data, it should have a 32-bit `type` field, and associated with an appropriately sized value buffer. The internal event queue will be updated everytime `draw_frame` is called.
36+
Allocate a continuous memory chunk that contains two tightly-packed queues, event queue and submission queue, with the specified capacity, the base address of the former queue is reside in the returned address, and the latter is followed right after the event queue's last element, which is the event queue's base address plus the size of each event element times the given capacity. The provided event counter variable is used to inform the user that an event is pushed to the event queue, its value will be increased whenever there is a new event, so initializing it before passing its address to this system call is critical.
3737

38-
Currently accepted event types include the following:
39-
* `KEY_EVENT` - `0x0`: Triggered when the status of keyboard changes, either when a key is pressed or released, and it returns a 32-bit keycode and a 8-bit key state, the values of the hexadecimal keycodes are listed in [SDL Keycode Lookup Table](https://wiki.libsdl.org/SDLKeycodeLookup)
40-
* `MOUSE_MOTION_EVENT` - `0x1`: A mouse move event, with relative position information, two 32-bit signed integers that correspond to the x and y delta value, the mouse is continually wrapped in the window border by default.
41-
* `MOUSE_BUTTON_EVENT` - `0x2`: the user code receives this event whenever the state of a mouse button changes, whether a button is pressed or released, and it returns a 8-bit value that indicates which button is updated(1 is left, 2 is middle, 3 is right and so on), as well as a 8-bit state value that indicates whether the button is pressed.
38+
#### Events
39+
40+
An event entry is made up of a 32-bit value representing the event's type and a `union` buffer containing t1he event's parameters.
41+
42+
* `KEY_EVENT`: Either a key is pressed or released. Its value buffer is made up of a 32-bit universal key code and an 8-bit state flag; if the corresponding character of the pressed key is not printable, the bit right after the most significant bit is set; for example, the "a" key's correspoding character is printable, so its keycode is the ASCII code of the "a" character, which is `0x61`. However, because the left shift key doesn't have a corresponding printable key, its hexadecimal value is `0x400000E1`, with the 31 bit is set.
43+
* `MOUSE_MOTION_EVENT`: The cursor is moved during the current frame. This event contains two signed integer value, which is the delta of x position and y position respectively. If the relative mouse mode is enabled, the mouse movement will never be 0 because the cursor is wrapped within the canvas and is repeated whenever the cursor reaches the border.
44+
* `MOUSE_BUTTON_EVENT`: The state of a mouse button has been changed. Its value buffer contains a 8-bit button value(1 is left, 2 is middle, 3 is right and so on) and an 8-bit boolean flag that indicates whether the mouse button is pressed.
45+
46+
### `submit_queue` - Notify the emulator a submission has been pushed into the submission queue
47+
48+
**system call number**: `0xFEED`
49+
50+
**synopsis**: `void submit_queue(int count)`
51+
52+
To inform the emulator that a batch of submissions should be processed, the application code should push several submissions into the queue first, and then pass the size of the submissions batch to this system call; the submissions will be processed and executed sequentially and immediately.
53+
54+
#### Submissions
55+
56+
The submission entry is structured similarly to an event entry, with a 32-bit type field and an associated dynamic-sized value buffer whose width depends on the type of submission.
57+
58+
* `RELATIVE_MODE_SUBMISSION`: Enable or disable the mouse relative mode. If the mouse relative mode is enabled, the mouse cursor is wrapped within the window border, it's associated with an 8-bit wide boolean value that indicates whether the relative mouse mode should be enbled.

src/syscall.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
#if RV32_HAS(SDL)
3838
#define __SYSCALL_LIST_EXT \
3939
_(draw_frame, 0xBEEF) \
40-
_(poll_event, 0xC0DE)
40+
_(setup_queue, 0xC0DE) \
41+
_(submit_queue, 0xFEED)
4142
#else
4243
#define __SYSCALL_LIST_EXT
4344
#endif
@@ -316,7 +317,8 @@ static void syscall_open(struct riscv_t *rv)
316317

317318
#if RV32_HAS(SDL)
318319
extern void syscall_draw_frame(struct riscv_t *rv);
319-
extern void syscall_poll_event(struct riscv_t *rv);
320+
extern void syscall_setup_queue(struct riscv_t *rv);
321+
extern void syscall_submit_queue(struct riscv_t *rv);
320322
#endif
321323

322324
void syscall_handler(struct riscv_t *rv)

src/syscall_sdl.c

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@
99

1010
#include "state.h"
1111

12-
/* For optimization, the capcity of event queues must be the power of two to
13-
* avoid the expensive modulo operation, the details are explained here:
14-
* https://stackoverflow.com/questions/10527581/why-must-a-ring-buffer-size-be-a-power-of-2
15-
*/
16-
#define EVENT_QUEUE_CAPACITY 128
17-
1812
enum {
1913
KEY_EVENT = 0,
2014
MOUSE_MOTION_EVENT = 1,
@@ -47,38 +41,65 @@ typedef struct {
4741
} event_t;
4842

4943
typedef struct {
50-
event_t events[EVENT_QUEUE_CAPACITY];
51-
size_t start, end;
52-
bool full;
44+
event_t *base;
45+
size_t end;
5346
} event_queue_t;
5447

48+
enum {
49+
RELATIVE_MODE_SUBMISSION = 0,
50+
};
51+
52+
typedef struct {
53+
uint32_t type;
54+
union {
55+
union {
56+
uint8_t enabled;
57+
} mouse;
58+
};
59+
} submission_t;
60+
61+
typedef struct {
62+
submission_t *base;
63+
size_t start;
64+
} submission_queue_t;
65+
5566
static SDL_Window *window = NULL;
5667
static SDL_Renderer *renderer;
5768
static SDL_Texture *texture;
69+
static uint32_t queues_capacity = 0;
70+
static uint32_t event_count;
5871
static event_queue_t event_queue = {
59-
.events = {},
60-
.start = 0,
72+
.base = NULL,
6173
.end = 0,
62-
.full = false,
74+
};
75+
static submission_queue_t submission_queue = {
76+
.base = NULL,
77+
.start = 0,
6378
};
6479

65-
static bool event_pop(event_t *event)
80+
static submission_t submission_pop(void)
6681
{
67-
if (event_queue.start == event_queue.end)
68-
return false;
69-
*event = event_queue.events[event_queue.start++];
70-
event_queue.start &= EVENT_QUEUE_CAPACITY - 1;
71-
event_queue.full = false;
72-
return true;
82+
submission_t submission = submission_queue.base[submission_queue.start++];
83+
if (queues_capacity & 1)
84+
submission_queue.start %= queues_capacity;
85+
else
86+
submission_queue.start &= queues_capacity - 1;
87+
return submission;
7388
}
7489

75-
static void event_push(event_t event)
90+
static void event_push(struct riscv_t *rv, event_t event)
7691
{
77-
if (event_queue.full)
78-
return;
79-
event_queue.events[event_queue.end++] = event;
80-
event_queue.end &= EVENT_QUEUE_CAPACITY - 1;
81-
event_queue.full = (event_queue.start == event_queue.end);
92+
event_queue.base[event_queue.end++] = event;
93+
if (queues_capacity & 1)
94+
event_queue.end %= queues_capacity;
95+
else
96+
event_queue.end &= queues_capacity - 1;
97+
98+
state_t *s = rv_userdata(rv);
99+
uint32_t count;
100+
memory_read(s->mem, (void *) &count, event_count, sizeof(uint32_t));
101+
count += 1;
102+
memory_write(s->mem, event_count, (void *) &count, sizeof(uint32_t));
82103
}
83104

84105
/* check if we need to setup SDL and run event loop */
@@ -128,7 +149,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
128149
.state = (bool) (event.key.state == SDL_PRESSED),
129150
};
130151
memcpy(&new_event.key_event, &key_event, sizeof(key_event));
131-
event_push(new_event);
152+
event_push(rv, new_event);
132153
break;
133154
}
134155
case SDL_MOUSEMOTION: {
@@ -141,7 +162,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
141162
};
142163
memcpy(&new_event.mouse.motion, &mouse_motion,
143164
sizeof(mouse_motion));
144-
event_push(new_event);
165+
event_push(rv, new_event);
145166
break;
146167
}
147168
case SDL_MOUSEBUTTONDOWN:
@@ -161,7 +182,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
161182
};
162183
memcpy(&new_event.mouse.button, &mouse_button,
163184
sizeof(mouse_button));
164-
event_push(new_event);
185+
event_push(rv, new_event);
165186
break;
166187
}
167188
}
@@ -193,34 +214,39 @@ void syscall_draw_frame(struct riscv_t *rv)
193214
SDL_RenderPresent(renderer);
194215
}
195216

196-
void syscall_poll_event(struct riscv_t *rv)
217+
void syscall_setup_queue(struct riscv_t *rv)
197218
{
198-
state_t *s = rv_userdata(rv); /* access userdata */
199-
200-
/* poll_event(event) */
201-
const uint32_t base = rv_get_reg(rv, rv_reg_a0);
202-
203-
event_t event;
204-
if (!event_pop(&event)) {
219+
/* setup_queue(capacity, event_count) */
220+
queues_capacity = rv_get_reg(rv, rv_reg_a0);
221+
event_count = rv_get_reg(rv, rv_reg_a1);
222+
if (queues_capacity == 0) {
205223
rv_set_reg(rv, rv_reg_a0, 0);
206224
return;
207225
}
208226

209-
memory_write(s->mem, base + 0, (const uint8_t *) &event.type, 4);
210-
switch (event.type) {
211-
case KEY_EVENT:
212-
memory_write(s->mem, base + 4, (const uint8_t *) &event.key_event,
213-
sizeof(key_event_t));
214-
break;
215-
case MOUSE_MOTION_EVENT:
216-
memory_write(s->mem, base + 4, (const uint8_t *) &event.mouse.motion,
217-
sizeof(mouse_motion_t));
218-
break;
219-
case MOUSE_BUTTON_EVENT:
220-
memory_write(s->mem, base + 4, (const uint8_t *) &event.mouse.button,
221-
sizeof(mouse_button_t));
222-
break;
223-
}
227+
void *base = malloc(sizeof(event_t) * queues_capacity +
228+
sizeof(submission_t) * queues_capacity);
229+
event_queue.base = base;
230+
submission_queue.base = base + sizeof(event_t) * queues_capacity;
224231

225-
rv_set_reg(rv, rv_reg_a0, 1);
232+
rv_set_reg(
233+
rv, rv_reg_a0,
234+
(uint32_t) (uintptr_t) base); /* eliminate the "cast from pointer to
235+
integer of different size" warning*/
226236
}
237+
238+
void syscall_submit_queue(struct riscv_t *rv)
239+
{
240+
/* submit(count) */
241+
uint32_t count = rv_get_reg(rv, rv_reg_a0);
242+
243+
while (count--) {
244+
submission_t submission = submission_pop();
245+
246+
switch (submission.type) {
247+
case RELATIVE_MODE_SUBMISSION:
248+
SDL_SetRelativeMouseMode(submission.mouse.enabled);
249+
break;
250+
}
251+
}
252+
}

0 commit comments

Comments
 (0)