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