Skip to main content

user_lib/
lib.rs

1#![no_std]
2//!
3//! 教程阅读建议:
4//!
5//! - `_start` 展示用户程序最小运行时(初始化控制台、堆、调用 main);
6//! - 其余辅助函数(sleep/pipe_*)展示了常见 syscall 组合用法。
7
8mod heap;
9
10extern crate alloc;
11
12use tg_console::log;
13
14pub use tg_console::{print, println};
15pub use tg_syscall::*;
16
17#[cfg(feature = "tangram")]
18pub mod tangram;
19
20#[cfg(feature = "snake")]
21pub mod snake;
22
23#[cfg(feature = "tetris")]
24pub mod tetris;
25
26#[cfg(feature = "pingpong")]
27pub mod pingpong;
28
29#[cfg(feature = "breakout")]
30pub mod breakout;
31
32#[cfg(feature = "pacman")]
33pub mod pacman;
34
35#[cfg(feature = "doom")]
36pub mod doom;
37
38/// Query framebuffer dimensions from kernel.
39/// Returns (width, height).
40pub fn fb_info() -> (u32, u32) {
41    let packed: usize;
42    unsafe {
43        core::arch::asm!(
44            "ecall",
45            in("a7") 2000usize,
46            lateout("a0") packed,
47        );
48    }
49    let width = (packed >> 32) as u32;
50    let height = packed as u32;
51    (width, height)
52}
53
54/// Write a rectangular BGRA pixel region to the kernel framebuffer.
55/// Does NOT flush — call `fb_flush()` after all writes for the frame.
56pub fn fb_write(x: u32, y: u32, w: u32, h: u32, data: *const u8) -> isize {
57    let ret: isize;
58    unsafe {
59        core::arch::asm!(
60            "ecall",
61            in("a7") 2001usize,
62            inlateout("a0") x as usize => ret,
63            in("a1") y as usize,
64            in("a2") w as usize,
65            in("a3") h as usize,
66            in("a4") data as usize,
67        );
68    }
69    ret
70}
71
72/// Request a shared memory page from the kernel.
73/// Returns the virtual address of the shared page, or usize::MAX on failure.
74pub fn shm_create() -> usize {
75    let ret: usize;
76    unsafe {
77        core::arch::asm!(
78            "ecall",
79            in("a7") 2002usize,
80            lateout("a0") ret,
81        );
82    }
83    ret
84}
85
86/// Flush the GPU framebuffer to the display.
87/// Call once per frame after all fb_write calls.
88pub fn fb_flush() {
89    unsafe {
90        core::arch::asm!(
91            "ecall",
92            in("a7") 2003usize,
93            lateout("a0") _,
94        );
95    }
96}
97#[unsafe(no_mangle)]
98#[unsafe(link_section = ".text.entry")]
99pub extern "C" fn _start() -> ! {
100    // 用户态运行时初始化顺序与内核类似:先 I/O,再堆,再进入 main。
101    tg_console::init_console(&Console);
102    tg_console::set_log_level(option_env!("LOG"));
103    heap::init();
104
105    unsafe extern "C" {
106        fn main() -> i32;
107    }
108
109    // SAFETY: main 函数由用户程序提供,链接器保证其存在且符合 C ABI
110    exit(unsafe { main() });
111    unreachable!()
112}
113
114#[panic_handler]
115fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
116    let err = panic_info.message();
117    if let Some(location) = panic_info.location() {
118        log::error!("Panicked at {}:{}, {err}", location.file(), location.line());
119    } else {
120        log::error!("Panicked: {err}");
121    }
122    exit(1);
123    unreachable!()
124}
125
126/// File descriptor for buffered keyboard input (timer-polled ring buffer in kernel).
127pub const STDIN_BUFFERED: usize = 3;
128
129pub fn getchar() -> u8 {
130    let mut c = [0u8; 1];
131    loop {
132        let n = read(STDIN, &mut c);
133        if n > 0 && c[0] != 0 {
134            return c[0];
135        }
136        sched_yield();
137    }
138}
139
140struct Console;
141
142impl tg_console::Console for Console {
143    #[inline]
144    fn put_char(&self, c: u8) {
145        tg_syscall::write(STDOUT, &[c]);
146    }
147
148    #[inline]
149    fn put_str(&self, s: &str) {
150        tg_syscall::write(STDOUT, s.as_bytes());
151    }
152}
153
154pub fn sleep(period_ms: usize) {
155    // 轮询时钟 + 主动让出 CPU 的教学实现,便于理解 time/yield 系统调用协作。
156    let mut time: TimeSpec = TimeSpec::ZERO;
157    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
158    let time = time + TimeSpec::from_millsecond(period_ms);
159    loop {
160        let mut now: TimeSpec = TimeSpec::ZERO;
161        clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _);
162        if now > time {
163            break;
164        }
165        sched_yield();
166    }
167}
168
169pub fn get_time() -> isize {
170    let mut time: TimeSpec = TimeSpec::ZERO;
171    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
172    (time.tv_sec * 1000 + time.tv_nsec / 1_000_000) as isize
173}
174
175pub fn trace_read(ptr: *const u8) -> Option<u8> {
176    let ret = trace(0, ptr as usize, 0);
177    if ret >= 0 && ret <= 255 {
178        Some(ret as u8)
179    } else {
180        None
181    }
182}
183
184pub fn trace_write(ptr: *const u8, value: u8) -> isize {
185    trace(1, ptr as usize, value as usize)
186}
187
188pub fn count_syscall(syscall_id: usize) -> isize {
189    trace(2, syscall_id, 0)
190}
191
192/// 从管道读取数据
193/// 返回实际读取的总字节数,负数表示错误
194pub fn pipe_read(pipe_fd: usize, buffer: &mut [u8]) -> isize {
195    let mut total_read = 0usize;
196    let len = buffer.len();
197    loop {
198        if total_read >= len {
199            return total_read as isize;
200        }
201        let ret = read(pipe_fd, &mut buffer[total_read..]);
202        if ret == -2 {
203            // 暂时无数据,让出 CPU 后重试
204            sched_yield();
205            continue;
206        } else if ret == 0 {
207            // EOF,写端关闭
208            return total_read as isize;
209        } else if ret < 0 {
210            // 其他错误
211            return ret;
212        } else {
213            total_read += ret as usize;
214        }
215    }
216}
217
218/// 向管道写入数据
219/// 返回实际写入的总字节数,负数表示错误
220pub fn pipe_write(pipe_fd: usize, buffer: &[u8]) -> isize {
221    let mut total_write = 0usize;
222    let len = buffer.len();
223    loop {
224        if total_write >= len {
225            return total_write as isize;
226        }
227        let ret = write(pipe_fd, &buffer[total_write..]);
228        if ret == -2 {
229            // 缓冲区满,让出 CPU 后重试
230            sched_yield();
231            continue;
232        } else if ret < 0 {
233            // 其他错误
234            return ret;
235        } else {
236            total_write += ret as usize;
237        }
238    }
239}