Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
99c85f6
Add source pid to event
automas-dev Mar 17, 2025
2c19f6b
Add process state and entrypoint
automas-dev Mar 17, 2025
27fd40d
Move process manager to new file
automas-dev Mar 17, 2025
3aec43e
Move queue_event to proc.h, add yield
automas-dev Mar 17, 2025
706c862
Handle system call for yield
automas-dev Mar 17, 2025
dadb884
Keep args as pointer
automas-dev Mar 17, 2025
3436059
Working idle process
automas-dev Mar 17, 2025
6e0ac7d
Add kernel panic, can load and start applications
automas-dev Mar 17, 2025
e8f3665
Merge branch 'main' into poll_event
automas-dev Mar 17, 2025
59a3dee
Fix process test noreturn compile error
automas-dev Mar 17, 2025
f71d5d6
Working on kernel task switching
automas-dev Mar 18, 2025
0e98a07
Working towards task switching
automas-dev Mar 18, 2025
af7abfe
Revert early boot
automas-dev Mar 18, 2025
47cddd6
Add link script file as link dependency
automas-dev Mar 18, 2025
4c5b5f7
Back to working term
automas-dev Mar 18, 2025
536c096
Launching and killing first task
automas-dev Mar 18, 2025
5374fa0
Try using a call as proc fn
automas-dev Mar 18, 2025
899c99a
Add pid command
automas-dev Mar 19, 2025
344cec5
Add disk and tar to kernel, mount in kernel_entry
automas-dev Mar 19, 2025
e3e1b35
Add wip unused idle.c and idle.h
automas-dev Mar 22, 2025
ae8e2f1
Add process_activate
automas-dev Mar 22, 2025
05218ba
hybrid updated process manager
automas-dev Mar 22, 2025
504fa5e
Use new idle process
automas-dev Mar 22, 2025
dacad62
Mostly working process init
automas-dev Mar 22, 2025
a09d915
Working process loop
automas-dev Mar 22, 2025
ee4bf80
idk why it breaks at mmu_change_dir process.c:146
automas-dev Mar 22, 2025
5731c4a
Fix tests
automas-dev Mar 22, 2025
f558d40
Tmp disable queue_event test until I can figure out why it isn't working
automas-dev Mar 22, 2025
aa79378
Add test and docstring for process_set_entrypoint
automas-dev Mar 22, 2025
499f0b7
Add test and docsstring for process_activate
automas-dev Mar 22, 2025
4e7a7f8
Fix test name
automas-dev Mar 22, 2025
16b99b4
Rename entrypoint field to eip
automas-dev Mar 22, 2025
ea5f91e
Test and add docstring for process_yield, add eip to process_yield
automas-dev Mar 22, 2025
6be43a4
Add test and docstring for process_resume
automas-dev Mar 22, 2025
1a176a9
Add test for _sys_yield in libk
automas-dev Mar 22, 2025
a7d6240
Test libc queue_event, yield and getpid
automas-dev Mar 22, 2025
d3f53a7
Stepping through functions now
automas-dev Mar 23, 2025
212d11d
Change isr to kpanic
automas-dev Mar 23, 2025
cf8ee21
Print pid for each task
automas-dev Mar 23, 2025
ef245a5
wip re-write task switch
automas-dev Mar 23, 2025
50c1f47
getting closer
automas-dev Mar 23, 2025
a55d1ec
Might be getting further
automas-dev Mar 23, 2025
fa5b1c1
TASK SWITCH WORKS!
automas-dev Mar 23, 2025
bbd4b8e
Format
automas-dev Mar 23, 2025
6627ff1
cleanup
automas-dev Mar 23, 2025
ebc3fc6
Updating tests
automas-dev Mar 23, 2025
d58201a
More tests
automas-dev Mar 23, 2025
6472022
Add coverage of resume state before loaded
automas-dev Mar 23, 2025
182dbd0
Finish tests for updated process
automas-dev Mar 23, 2025
75b4687
Never return to kernel task
automas-dev Mar 23, 2025
ce5618c
Remove filter from yield, replace pull_event with system call
automas-dev Mar 23, 2025
7bec77f
ebus pop and return from yield
automas-dev Mar 23, 2025
7aade81
Return errors for ebus_push, cleanup ebus_pop, filter push events
automas-dev Mar 23, 2025
1bbac1b
Working terminal, but slow
automas-dev Mar 23, 2025
eb54e7b
Set source pid in system call handler
automas-dev Mar 24, 2025
709aa7e
Update mocks and tests
automas-dev Mar 24, 2025
a439402
Fix key repeat and state key state
automas-dev Mar 24, 2025
6c19930
wip update command_exec
automas-dev Mar 24, 2025
7eff8d9
Enable timer and rtc
automas-dev Mar 24, 2025
6162af4
cleanup
automas-dev Mar 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmake/targets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function(cross_target_binary target)
-nostdlib
"-L${CROSS_PREFIX}/lib/gcc/i386-elf/12.2.0"
-lgcc
DEPENDS ${target} ${TARGET_LINK_FILES})
DEPENDS ${target} ${TARGET_LINK_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/link.ld)

add_custom_target(${target}_image ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${target}.bin)

Expand Down
2 changes: 1 addition & 1 deletion src/apps/foo/src/foo.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
void foo() {
uint32_t res = puts("Hello\n");

proc_exit(0);
pull_event(EBUS_EVENT_KEY, 0);

res = printf("Hello 0x%X\n", res);

Expand Down
82 changes: 0 additions & 82 deletions src/cpu/i386/src/cpu.asm
Original file line number Diff line number Diff line change
Expand Up @@ -31,85 +31,3 @@ flush_tss:
mov ax, (5 * 8) | 0 ; fifth 8-byte selector, symbolically OR-ed with 0 to set the RPL (requested privilege level).
ltr ax
ret

global jump_usermode
jump_usermode:
mov ebx, [esp+4]
mov ax, (4 * 8) | 3 ; ring 3 data with bottom 2 bits set for ring 3
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax ; SS is handled by iret

; set up the stack frame iret expects
mov eax, esp
push (4 * 8) | 3 ; data selector
push eax ; current esp
pushf ; eflags
push (3 * 8) | 3 ; code selector (ring 3 code with bottom 2 bits set for ring 3)
push ebx ; instruction address to return to
iret

; https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial

;C declaration:
; void switch_to_task(thread_control_block *next_thread);
;
;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns

TCB_EIP equ 4
TCB_ESP equ 8
TCB_CR3 equ 12
TCB_ESP0 equ 16
TSS_ESP0 equ 20

current_task_TCB: dd 0

global set_first_task
set_first_task:
mov eax, [esp + 4]
mov [current_task_TCB], eax
ret

global switch_to_task
switch_to_task:

;Save previous task's state

;Notes:
; For cdecl; EAX, ECX, and EDX are already saved by the caller and don't need to be saved again
; EIP is already saved on the stack by the caller's "CALL" instruction
; The task isn't able to change CR3 so it doesn't need to be saved
; Segment registers are constants (while running kernel code) so they don't need to be saved

push ebx
push esi
push edi
push ebp

mov edi,[current_task_TCB] ;edi = address of the previous task's "thread control block"
mov [edi+TCB_ESP],esp ;Save ESP for previous task's kernel stack in the thread's TCB

;Load next task's state

mov esi,[esp+(4+1)*4] ;esi = address of the next task's "thread control block" (parameter passed on stack)
mov [current_task_TCB],esi ;Current task's TCB is the next task TCB

mov esp,[esi+TCB_ESP] ;Load ESP for next task's kernel stack from the thread's TCB
mov eax,[esi+TCB_CR3] ;eax = address of page directory for next task
mov ebx,[esi+TCB_ESP0] ;ebx = address for the top of the next task's kernel stack
mov [TSS_ESP0],ebx ;Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes)
mov ecx,cr3 ;ecx = previous task's virtual address space

cmp eax,ecx ;Does the virtual address space need to being changed?
je .doneVAS ; no, virtual address space is the same, so don't reload it and cause TLB flushes
mov cr3,eax ; yes, load the next task's virtual address space

.doneVAS:

pop ebp
pop edi
pop esi
pop ebx

ret ;Load next task's EIP from its kernel stack
5 changes: 3 additions & 2 deletions src/cpu/i386/src/isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "cpu/idt.h"
#include "cpu/ports.h"
#include "kernel.h"
#include "libc/proc.h"
#include "libc/stdio.h"

Expand Down Expand Up @@ -161,7 +162,7 @@ void isr_handler(registers_t r) {
default:
break;
}
PANIC("STOP HERE");
KPANIC("STOP HERE");
}

/*
Expand Down Expand Up @@ -191,7 +192,7 @@ void irq_handler(registers_t r) {
if (r.int_no >= 256) {
printf("BAD INTERRUPT 0x%X\n", r.int_no);
print_trace(&r);
PANIC("BAD INTERRUPT");
KPANIC("BAD INTERRUPT");
return;
}

Expand Down
6 changes: 5 additions & 1 deletion src/cpu/i386/src/tss.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ tss_entry_t * tss_get_entry(size_t i) {
return &tss_stack[i];
}

void set_kernel_stack(uint32_t stack) { // Used when an interrupt occurs
uint32_t tss_get_esp0() {
return tss_stack[0].esp0;
}

void tss_set_esp0(uint32_t stack) { // Used when an interrupt occurs
tss_stack[0].esp0 = stack;
}
3 changes: 2 additions & 1 deletion src/cpu/include/cpu/tss.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void init_tss();

tss_entry_t * tss_get_entry(size_t i);

void set_kernel_stack(uint32_t stack);
uint32_t tss_get_esp0(void);
void tss_set_esp0(uint32_t stack);

#endif // TSS_H
2 changes: 0 additions & 2 deletions src/drivers/include/drivers/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,4 @@ typedef enum keyboard_key {

void init_keyboard();

char keyboard_char(uint8_t code, bool shift);

#endif // KEYBOARD_H
138 changes: 79 additions & 59 deletions src/drivers/src/keyboard.c
Original file line number Diff line number Diff line change
@@ -1,70 +1,104 @@
#include "drivers/keyboard.h"

#include <stdbool.h>

#include "cpu/isr.h"
#include "cpu/ports.h"
#include "libc/signal.h"
#include "libc/proc.h"
#include "libc/stdio.h"
#include "libc/string.h"

static bool e0_mode;
static uint32_t keystate[8];
static char keyMap[0xFF] = {0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '+', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static char shiftKeyMap[0xFF] = {0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static void keyboard_callback(registers_t * regs);
static char keyboard_char(uint8_t code, bool shift);
static bool get_key_state(uint8_t keycode);
static void set_key_state(uint8_t keycode, bool state);
static uint8_t get_mods();

void init_keyboard() {
e0_mode = false;
kmemset(keystate, 0, sizeof(keystate));
register_interrupt_handler(IRQ1, keyboard_callback);
}

static bool get_key_state(uint8_t keycode) {
int i = keycode / 32;
int b = keycode % 32;

static uint8_t last_code = 0;
static bool lctrl = false;
static bool rctrl = false;
static bool lalt = false;
static bool ralt = false;
static bool lshift = false;
static bool rshift = false;
static bool lsuper = false;
static bool rsuper = false;
return keystate[i] & (1 << b);
}

static void set_key_state(uint8_t keycode, bool state) {
int i = keycode / 32;
int b = keycode % 32;

if (state) {
keystate[i] |= (1 << b);
}
else {
keystate[i] &= ~(1 << b);
}
}

static uint8_t get_mods() {
uint8_t mods = 0;
// TODO handle 0xE0 to get right ctrl
if (get_key_state(KEY_LCTRL)) {
mods |= KEY_MOD_CTRL;
}
// TODO handle 0xE0 to get right alt
if (get_key_state(KEY_LALT)) {
mods |= KEY_MOD_ALT;
}
if (get_key_state(KEY_LSHIFT) || get_key_state(KEY_RSHIFT)) {
mods |= KEY_MOD_SHIFT;
}
// TODO handle 0xE0 to get right super
if (get_key_state(KEY_SUPER)) {
mods |= KEY_MOD_SUPER;
}
return mods;
}

static char keyboard_char(uint8_t code, bool shift) {
code = code & 0x7F;
if (shift) {
return shiftKeyMap[code];
}
return keyMap[code];
}

static void keyboard_callback(registers_t * regs) {
/* The PIC leaves us the scancode in port 0x60 */
uint8_t scancode = port_byte_in(0x60);
uint8_t scancode = port_byte_in(0x60);
if (scancode == 0xE0) {
e0_mode = true;
return;
}
// printf("%02X ", scancode);
uint8_t keycode = scancode;
keyboard_event_t key_event = KEY_EVENT_PRESS;
uint8_t mods = get_mods();
bool press = keycode < 0x80;

if (press) {
if (keycode == last_code) {
if (get_key_state(keycode)) {
key_event = KEY_EVENT_REPEAT;
}
else {
if (keycode == KEY_LSHIFT) {
lshift = true;
}
if (keycode == KEY_RSHIFT) {
lshift = true;
}
}
last_code = keycode;
set_key_state(keycode, true);
}

else {
keycode -= 0x80;
key_event = KEY_EVENT_RELEASE;
last_code = 0;

if (keycode == KEY_LSHIFT) {
lshift = false;
}
if (keycode == KEY_RSHIFT) {
lshift = false;
}
set_key_state(keycode, false);
}

char c = keyboard_char(keycode, lshift || rshift);

keyboard_mod_t mods = 0;
if (lctrl || rctrl) {
mods |= KEY_MOD_CTRL;
}
if (lalt || ralt) {
mods |= KEY_MOD_ALT;
}
if (lshift || rshift) {
mods |= KEY_MOD_SHIFT;
}
if (lsuper || rsuper) {
mods |= KEY_MOD_SUPER;
}
char c = keyboard_char(keycode, mods & KEY_MOD_SHIFT);

ebus_event_t event;
event.event_id = EBUS_EVENT_KEY;
Expand All @@ -74,19 +108,5 @@ static void keyboard_callback(registers_t * regs) {
event.key.keycode = keycode;
event.key.scancode = scancode;
queue_event(&event);
}

void init_keyboard() {
register_interrupt_handler(IRQ1, keyboard_callback);
}

char keyMap[0xFF] = {0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '+', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char shiftKeyMap[0xFF] = {0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

char keyboard_char(uint8_t code, bool shift) {
code = code & 0x7F;
if (shift) {
return shiftKeyMap[code];
}
return keyMap[code];
e0_mode = false;
}
2 changes: 1 addition & 1 deletion src/drivers/src/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "cpu/ports.h"
#include "ebus.h"
#include "libc/datastruct/array.h"
#include "libc/signal.h"
#include "libc/proc.h"
#include "libc/stdio.h"

// https://wiki.osdev.org/PIT
Expand Down
2 changes: 1 addition & 1 deletion src/ebus/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(TARGET ebus)

cross_target(${TARGET})
target_link_libraries(${TARGET} libc)
target_link_libraries(${TARGET} libc kernel)
18 changes: 15 additions & 3 deletions src/ebus/include/ebus.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,35 @@ enum EBUS_EVENT {
EBUS_EVENT_ANY = 0,
EBUS_EVENT_TIMER,
EBUS_EVENT_KEY,
EBUS_EVENT_TASK_SWITCH,
EBUS_EVENT_TASK_KILL,
};

typedef struct _ebus_event {
int event_id;
int source_pid;
union {
struct {
int id;
uint32_t time;
} timer;
struct {
// raw = 0x83
// keycode = 0x03
// event = PRESS
// mods = SHIFT + ALT
uint8_t event;
uint8_t mods;
char c;
uint32_t keycode;
uint32_t scancode;
} key;
struct {
uint32_t next_task_pid;
} task_switch;
struct {
uint32_t task_pid;
} task_kill;
};
} ebus_event_t;

Expand All @@ -56,8 +69,6 @@ typedef struct _ebus {
arr_t handlers; // ebus_handler_t
cb_t queue; // ebus_event_t

int enabled;

int next_handler_id;
} ebus_t;

Expand All @@ -77,7 +88,8 @@ int ebus_queue_size(ebus_t * bus);
int ebus_register_handler(ebus_t * bus, ebus_handler_t * handler);
void ebus_unregister_handler(ebus_t * bus, int handler_id);

void ebus_push(ebus_t * bus, ebus_event_t * event);
int ebus_push(ebus_t * bus, ebus_event_t * event);
int ebus_pop(ebus_t * bus, ebus_event_t * event_out);

int ebus_cycle(ebus_t * bus);

Expand Down
Loading