+2
-7
aethelos-source/heartwood/src/attunement/idt_handlers.rs
+2
-7
aethelos-source/heartwood/src/attunement/idt_handlers.rs
···
247
247
}
248
248
249
249
extern "C" fn keyboard_inner() {
250
-
// Will be connected to keyboard driver
251
-
// For now, just read and discard the scancode
252
-
unsafe {
253
-
let scancode: u8;
254
-
asm!("in al, 0x60", out("al") scancode, options(nostack, preserves_flags));
255
-
// TODO: Send to keyboard driver via Nexus
256
-
}
250
+
// Handle keyboard interrupt via the keyboard driver
251
+
crate::attunement::keyboard::on_interrupt();
257
252
}
258
253
259
254
// Stub handlers for other hardware interrupts
+507
aethelos-source/heartwood/src/attunement/keyboard.rs
+507
aethelos-source/heartwood/src/attunement/keyboard.rs
···
1
+
//! # PS/2 Keyboard Driver
2
+
//!
3
+
//! The voice of the user - how humans commune with AethelOS.
4
+
//! Each keystroke is a word in the conversation between human and machine.
5
+
//!
6
+
//! ## Philosophy
7
+
//! Input is not commands to be obeyed, but intentions to be understood.
8
+
//! We listen to the keyboard not as servants awaiting orders,
9
+
//! but as partners engaged in dialogue.
10
+
//!
11
+
//! ## Technical Details
12
+
//! The PS/2 keyboard controller uses IRQ 1 (interrupt 33 after remapping).
13
+
//! Scancodes arrive as single or multi-byte sequences via port 0x60.
14
+
//! We translate scan codes (Set 1) into meaningful key events.
15
+
16
+
use core::arch::asm;
17
+
use lazy_static::lazy_static;
18
+
use spin::Mutex;
19
+
20
+
/// PS/2 keyboard data port
21
+
const KEYBOARD_DATA: u16 = 0x60;
22
+
23
+
/// PS/2 keyboard status/command port
24
+
const KEYBOARD_STATUS: u16 = 0x64;
25
+
26
+
/// Status register flags
27
+
const STATUS_OUTPUT_FULL: u8 = 0x01;
28
+
29
+
lazy_static! {
30
+
/// Global keyboard state
31
+
pub static ref KEYBOARD: Mutex<Keyboard> = Mutex::new(Keyboard::new());
32
+
}
33
+
34
+
/// Keyboard modifier state
35
+
#[derive(Debug, Clone, Copy, Default)]
36
+
pub struct Modifiers {
37
+
pub left_shift: bool,
38
+
pub right_shift: bool,
39
+
pub left_ctrl: bool,
40
+
pub right_ctrl: bool,
41
+
pub left_alt: bool,
42
+
pub right_alt: bool,
43
+
pub caps_lock: bool,
44
+
pub num_lock: bool,
45
+
pub scroll_lock: bool,
46
+
}
47
+
48
+
impl Modifiers {
49
+
/// Check if any shift key is pressed
50
+
pub fn shift(&self) -> bool {
51
+
self.left_shift || self.right_shift
52
+
}
53
+
54
+
/// Check if any ctrl key is pressed
55
+
pub fn ctrl(&self) -> bool {
56
+
self.left_ctrl || self.right_ctrl
57
+
}
58
+
59
+
/// Check if any alt key is pressed
60
+
pub fn alt(&self) -> bool {
61
+
self.left_alt || self.right_alt
62
+
}
63
+
}
64
+
65
+
/// A keyboard event
66
+
#[derive(Debug, Clone, Copy)]
67
+
pub enum KeyEvent {
68
+
/// Key was pressed
69
+
Pressed(Key),
70
+
/// Key was released
71
+
Released(Key),
72
+
}
73
+
74
+
/// Represents a key on the keyboard
75
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76
+
pub enum Key {
77
+
// Letters
78
+
A, B, C, D, E, F, G, H, I, J, K, L, M,
79
+
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
80
+
81
+
// Numbers
82
+
Num0, Num1, Num2, Num3, Num4,
83
+
Num5, Num6, Num7, Num8, Num9,
84
+
85
+
// Function keys
86
+
F1, F2, F3, F4, F5, F6,
87
+
F7, F8, F9, F10, F11, F12,
88
+
89
+
// Special keys
90
+
Escape,
91
+
Backspace,
92
+
Tab,
93
+
Enter,
94
+
Space,
95
+
96
+
// Modifiers
97
+
LeftShift,
98
+
RightShift,
99
+
LeftCtrl,
100
+
RightCtrl,
101
+
LeftAlt,
102
+
RightAlt,
103
+
104
+
// Lock keys
105
+
CapsLock,
106
+
NumLock,
107
+
ScrollLock,
108
+
109
+
// Navigation
110
+
Insert,
111
+
Delete,
112
+
Home,
113
+
End,
114
+
PageUp,
115
+
PageDown,
116
+
ArrowUp,
117
+
ArrowDown,
118
+
ArrowLeft,
119
+
ArrowRight,
120
+
121
+
// Punctuation and symbols
122
+
Minus, // -
123
+
Equal, // =
124
+
LeftBracket, // [
125
+
RightBracket, // ]
126
+
Backslash, // \
127
+
Semicolon, // ;
128
+
Quote, // '
129
+
Grave, // `
130
+
Comma, // ,
131
+
Period, // .
132
+
Slash, // /
133
+
134
+
// Numpad
135
+
NumpadDivide,
136
+
NumpadMultiply,
137
+
NumpadMinus,
138
+
NumpadPlus,
139
+
NumpadEnter,
140
+
NumpadPeriod,
141
+
Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
142
+
Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
143
+
144
+
// Unknown key
145
+
Unknown(u8),
146
+
}
147
+
148
+
impl Key {
149
+
/// Convert key to ASCII character if possible
150
+
pub fn to_ascii(&self, modifiers: &Modifiers) -> Option<char> {
151
+
let shift = modifiers.shift() ^ modifiers.caps_lock;
152
+
153
+
match self {
154
+
// Letters
155
+
Key::A => Some(if shift { 'A' } else { 'a' }),
156
+
Key::B => Some(if shift { 'B' } else { 'b' }),
157
+
Key::C => Some(if shift { 'C' } else { 'c' }),
158
+
Key::D => Some(if shift { 'D' } else { 'd' }),
159
+
Key::E => Some(if shift { 'E' } else { 'e' }),
160
+
Key::F => Some(if shift { 'F' } else { 'f' }),
161
+
Key::G => Some(if shift { 'G' } else { 'g' }),
162
+
Key::H => Some(if shift { 'H' } else { 'h' }),
163
+
Key::I => Some(if shift { 'I' } else { 'i' }),
164
+
Key::J => Some(if shift { 'J' } else { 'j' }),
165
+
Key::K => Some(if shift { 'K' } else { 'k' }),
166
+
Key::L => Some(if shift { 'L' } else { 'l' }),
167
+
Key::M => Some(if shift { 'M' } else { 'm' }),
168
+
Key::N => Some(if shift { 'N' } else { 'n' }),
169
+
Key::O => Some(if shift { 'O' } else { 'o' }),
170
+
Key::P => Some(if shift { 'P' } else { 'p' }),
171
+
Key::Q => Some(if shift { 'Q' } else { 'q' }),
172
+
Key::R => Some(if shift { 'R' } else { 'r' }),
173
+
Key::S => Some(if shift { 'S' } else { 's' }),
174
+
Key::T => Some(if shift { 'T' } else { 't' }),
175
+
Key::U => Some(if shift { 'U' } else { 'u' }),
176
+
Key::V => Some(if shift { 'V' } else { 'v' }),
177
+
Key::W => Some(if shift { 'W' } else { 'w' }),
178
+
Key::X => Some(if shift { 'X' } else { 'x' }),
179
+
Key::Y => Some(if shift { 'Y' } else { 'y' }),
180
+
Key::Z => Some(if shift { 'Z' } else { 'z' }),
181
+
182
+
// Numbers
183
+
Key::Num0 => Some(if modifiers.shift() { ')' } else { '0' }),
184
+
Key::Num1 => Some(if modifiers.shift() { '!' } else { '1' }),
185
+
Key::Num2 => Some(if modifiers.shift() { '@' } else { '2' }),
186
+
Key::Num3 => Some(if modifiers.shift() { '#' } else { '3' }),
187
+
Key::Num4 => Some(if modifiers.shift() { '$' } else { '4' }),
188
+
Key::Num5 => Some(if modifiers.shift() { '%' } else { '5' }),
189
+
Key::Num6 => Some(if modifiers.shift() { '^' } else { '6' }),
190
+
Key::Num7 => Some(if modifiers.shift() { '&' } else { '7' }),
191
+
Key::Num8 => Some(if modifiers.shift() { '*' } else { '8' }),
192
+
Key::Num9 => Some(if modifiers.shift() { '(' } else { '9' }),
193
+
194
+
// Special keys
195
+
Key::Space => Some(' '),
196
+
Key::Enter => Some('\n'),
197
+
Key::Tab => Some('\t'),
198
+
Key::Backspace => Some('\x08'),
199
+
200
+
// Punctuation
201
+
Key::Minus => Some(if modifiers.shift() { '_' } else { '-' }),
202
+
Key::Equal => Some(if modifiers.shift() { '+' } else { '=' }),
203
+
Key::LeftBracket => Some(if modifiers.shift() { '{' } else { '[' }),
204
+
Key::RightBracket => Some(if modifiers.shift() { '}' } else { ']' }),
205
+
Key::Backslash => Some(if modifiers.shift() { '|' } else { '\\' }),
206
+
Key::Semicolon => Some(if modifiers.shift() { ':' } else { ';' }),
207
+
Key::Quote => Some(if modifiers.shift() { '"' } else { '\'' }),
208
+
Key::Grave => Some(if modifiers.shift() { '~' } else { '`' }),
209
+
Key::Comma => Some(if modifiers.shift() { '<' } else { ',' }),
210
+
Key::Period => Some(if modifiers.shift() { '>' } else { '.' }),
211
+
Key::Slash => Some(if modifiers.shift() { '?' } else { '/' }),
212
+
213
+
// Numpad (when numlock is on)
214
+
Key::Numpad0 => if modifiers.num_lock { Some('0') } else { None },
215
+
Key::Numpad1 => if modifiers.num_lock { Some('1') } else { None },
216
+
Key::Numpad2 => if modifiers.num_lock { Some('2') } else { None },
217
+
Key::Numpad3 => if modifiers.num_lock { Some('3') } else { None },
218
+
Key::Numpad4 => if modifiers.num_lock { Some('4') } else { None },
219
+
Key::Numpad5 => if modifiers.num_lock { Some('5') } else { None },
220
+
Key::Numpad6 => if modifiers.num_lock { Some('6') } else { None },
221
+
Key::Numpad7 => if modifiers.num_lock { Some('7') } else { None },
222
+
Key::Numpad8 => if modifiers.num_lock { Some('8') } else { None },
223
+
Key::Numpad9 => if modifiers.num_lock { Some('9') } else { None },
224
+
Key::NumpadPeriod => if modifiers.num_lock { Some('.') } else { None },
225
+
Key::NumpadDivide => Some('/'),
226
+
Key::NumpadMultiply => Some('*'),
227
+
Key::NumpadMinus => Some('-'),
228
+
Key::NumpadPlus => Some('+'),
229
+
Key::NumpadEnter => Some('\n'),
230
+
231
+
// Non-printable keys
232
+
_ => None,
233
+
}
234
+
}
235
+
}
236
+
237
+
/// PS/2 Keyboard state machine
238
+
pub struct Keyboard {
239
+
modifiers: Modifiers,
240
+
extended_scancode: bool,
241
+
}
242
+
243
+
impl Keyboard {
244
+
/// Create a new keyboard state
245
+
pub const fn new() -> Self {
246
+
Keyboard {
247
+
modifiers: Modifiers {
248
+
left_shift: false,
249
+
right_shift: false,
250
+
left_ctrl: false,
251
+
right_ctrl: false,
252
+
left_alt: false,
253
+
right_alt: false,
254
+
caps_lock: false,
255
+
num_lock: false,
256
+
scroll_lock: false,
257
+
},
258
+
extended_scancode: false,
259
+
}
260
+
}
261
+
262
+
/// Get current modifiers
263
+
pub fn modifiers(&self) -> Modifiers {
264
+
self.modifiers
265
+
}
266
+
267
+
/// Process a scancode byte and return a key event
268
+
pub fn process_scancode(&mut self, scancode: u8) -> Option<KeyEvent> {
269
+
// Handle extended scancodes (0xE0 prefix)
270
+
if scancode == 0xE0 {
271
+
self.extended_scancode = true;
272
+
return None;
273
+
}
274
+
275
+
// Check if this is a key release (high bit set)
276
+
let released = (scancode & 0x80) != 0;
277
+
let code = scancode & 0x7F;
278
+
279
+
// Translate scancode to key
280
+
let key = if self.extended_scancode {
281
+
self.extended_scancode = false;
282
+
match code {
283
+
0x1D => Key::RightCtrl,
284
+
0x38 => Key::RightAlt,
285
+
0x48 => Key::ArrowUp,
286
+
0x50 => Key::ArrowDown,
287
+
0x4B => Key::ArrowLeft,
288
+
0x4D => Key::ArrowRight,
289
+
0x49 => Key::PageUp,
290
+
0x51 => Key::PageDown,
291
+
0x47 => Key::Home,
292
+
0x4F => Key::End,
293
+
0x52 => Key::Insert,
294
+
0x53 => Key::Delete,
295
+
0x35 => Key::NumpadDivide,
296
+
_ => Key::Unknown(scancode),
297
+
}
298
+
} else {
299
+
scancode_to_key(code)
300
+
};
301
+
302
+
// Update modifier state
303
+
self.update_modifiers(&key, released);
304
+
305
+
// Return the event
306
+
if released {
307
+
Some(KeyEvent::Released(key))
308
+
} else {
309
+
Some(KeyEvent::Pressed(key))
310
+
}
311
+
}
312
+
313
+
/// Update modifier key states
314
+
fn update_modifiers(&mut self, key: &Key, released: bool) {
315
+
match key {
316
+
Key::LeftShift => self.modifiers.left_shift = !released,
317
+
Key::RightShift => self.modifiers.right_shift = !released,
318
+
Key::LeftCtrl => self.modifiers.left_ctrl = !released,
319
+
Key::RightCtrl => self.modifiers.right_ctrl = !released,
320
+
Key::LeftAlt => self.modifiers.left_alt = !released,
321
+
Key::RightAlt => self.modifiers.right_alt = !released,
322
+
323
+
// Toggle lock keys on press
324
+
Key::CapsLock if !released => self.modifiers.caps_lock = !self.modifiers.caps_lock,
325
+
Key::NumLock if !released => self.modifiers.num_lock = !self.modifiers.num_lock,
326
+
Key::ScrollLock if !released => self.modifiers.scroll_lock = !self.modifiers.scroll_lock,
327
+
328
+
_ => {}
329
+
}
330
+
}
331
+
}
332
+
333
+
/// Translate a scancode (Set 1) to a Key
334
+
fn scancode_to_key(code: u8) -> Key {
335
+
match code {
336
+
// Row 1
337
+
0x01 => Key::Escape,
338
+
0x3B => Key::F1,
339
+
0x3C => Key::F2,
340
+
0x3D => Key::F3,
341
+
0x3E => Key::F4,
342
+
0x3F => Key::F5,
343
+
0x40 => Key::F6,
344
+
0x41 => Key::F7,
345
+
0x42 => Key::F8,
346
+
0x43 => Key::F9,
347
+
0x44 => Key::F10,
348
+
0x57 => Key::F11,
349
+
0x58 => Key::F12,
350
+
351
+
// Row 2
352
+
0x29 => Key::Grave,
353
+
0x02 => Key::Num1,
354
+
0x03 => Key::Num2,
355
+
0x04 => Key::Num3,
356
+
0x05 => Key::Num4,
357
+
0x06 => Key::Num5,
358
+
0x07 => Key::Num6,
359
+
0x08 => Key::Num7,
360
+
0x09 => Key::Num8,
361
+
0x0A => Key::Num9,
362
+
0x0B => Key::Num0,
363
+
0x0C => Key::Minus,
364
+
0x0D => Key::Equal,
365
+
0x0E => Key::Backspace,
366
+
367
+
// Row 3
368
+
0x0F => Key::Tab,
369
+
0x10 => Key::Q,
370
+
0x11 => Key::W,
371
+
0x12 => Key::E,
372
+
0x13 => Key::R,
373
+
0x14 => Key::T,
374
+
0x15 => Key::Y,
375
+
0x16 => Key::U,
376
+
0x17 => Key::I,
377
+
0x18 => Key::O,
378
+
0x19 => Key::P,
379
+
0x1A => Key::LeftBracket,
380
+
0x1B => Key::RightBracket,
381
+
0x2B => Key::Backslash,
382
+
383
+
// Row 4
384
+
0x3A => Key::CapsLock,
385
+
0x1E => Key::A,
386
+
0x1F => Key::S,
387
+
0x20 => Key::D,
388
+
0x21 => Key::F,
389
+
0x22 => Key::G,
390
+
0x23 => Key::H,
391
+
0x24 => Key::J,
392
+
0x25 => Key::K,
393
+
0x26 => Key::L,
394
+
0x27 => Key::Semicolon,
395
+
0x28 => Key::Quote,
396
+
0x1C => Key::Enter,
397
+
398
+
// Row 5
399
+
0x2A => Key::LeftShift,
400
+
0x2C => Key::Z,
401
+
0x2D => Key::X,
402
+
0x2E => Key::C,
403
+
0x2F => Key::V,
404
+
0x30 => Key::B,
405
+
0x31 => Key::N,
406
+
0x32 => Key::M,
407
+
0x33 => Key::Comma,
408
+
0x34 => Key::Period,
409
+
0x35 => Key::Slash,
410
+
0x36 => Key::RightShift,
411
+
412
+
// Row 6
413
+
0x1D => Key::LeftCtrl,
414
+
0x38 => Key::LeftAlt,
415
+
0x39 => Key::Space,
416
+
417
+
// Numpad
418
+
0x45 => Key::NumLock,
419
+
0x46 => Key::ScrollLock,
420
+
0x47 => Key::Numpad7,
421
+
0x48 => Key::Numpad8,
422
+
0x49 => Key::Numpad9,
423
+
0x4A => Key::NumpadMinus,
424
+
0x4B => Key::Numpad4,
425
+
0x4C => Key::Numpad5,
426
+
0x4D => Key::Numpad6,
427
+
0x4E => Key::NumpadPlus,
428
+
0x4F => Key::Numpad1,
429
+
0x50 => Key::Numpad2,
430
+
0x51 => Key::Numpad3,
431
+
0x52 => Key::Numpad0,
432
+
0x53 => Key::NumpadPeriod,
433
+
0x37 => Key::NumpadMultiply,
434
+
435
+
// Unknown
436
+
_ => Key::Unknown(code),
437
+
}
438
+
}
439
+
440
+
/// Read a scancode from the keyboard data port
441
+
///
442
+
/// # Safety
443
+
/// Reads from I/O port 0x60. Should only be called from keyboard interrupt handler.
444
+
pub unsafe fn read_scancode() -> u8 {
445
+
inb(KEYBOARD_DATA)
446
+
}
447
+
448
+
/// Check if keyboard data is available
449
+
pub unsafe fn data_available() -> bool {
450
+
(inb(KEYBOARD_STATUS) & STATUS_OUTPUT_FULL) != 0
451
+
}
452
+
453
+
/// Helper function to input a byte from an I/O port
454
+
#[inline]
455
+
unsafe fn inb(port: u16) -> u8 {
456
+
let value: u8;
457
+
asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags));
458
+
value
459
+
}
460
+
461
+
/// Initialize the keyboard
462
+
pub fn init() {
463
+
// The PS/2 keyboard is initialized by the BIOS
464
+
// We just need to enable IRQ 1 and start receiving scancodes
465
+
unsafe {
466
+
super::PIC.lock().enable_irq(1);
467
+
}
468
+
}
469
+
470
+
/// Handle a keyboard interrupt (called from IRQ 1 handler)
471
+
pub fn on_interrupt() {
472
+
unsafe {
473
+
if data_available() {
474
+
let scancode = read_scancode();
475
+
let mut keyboard = KEYBOARD.lock();
476
+
477
+
if let Some(event) = keyboard.process_scancode(scancode) {
478
+
handle_key_event(event, &keyboard.modifiers());
479
+
}
480
+
}
481
+
}
482
+
}
483
+
484
+
/// Handle a key event (override this for custom behavior)
485
+
fn handle_key_event(event: KeyEvent, modifiers: &Modifiers) {
486
+
match event {
487
+
KeyEvent::Pressed(key) => {
488
+
if let Some(ch) = key.to_ascii(modifiers) {
489
+
// Echo printable characters to screen
490
+
if ch.is_ascii() && !ch.is_ascii_control() {
491
+
crate::print!("{}", ch);
492
+
} else if ch == '\n' {
493
+
crate::println!();
494
+
} else if ch == '\x08' {
495
+
// Backspace - would need VGA driver support
496
+
crate::print!("\x08 \x08");
497
+
}
498
+
} else {
499
+
// Non-ASCII key pressed
500
+
// Could log special keys for debugging
501
+
}
502
+
}
503
+
KeyEvent::Released(_) => {
504
+
// Usually we don't care about key releases for typing
505
+
}
506
+
}
507
+
}
+5
aethelos-source/heartwood/src/attunement/mod.rs
+5
aethelos-source/heartwood/src/attunement/mod.rs
···
11
11
12
12
pub mod gdt;
13
13
pub mod idt;
14
+
pub mod keyboard;
14
15
pub mod pic;
15
16
pub mod pit;
16
17
···
72
73
// Initialize system timer (PIT)
73
74
crate::println!(" ∴ Starting system heartbeat (PIT @ {} Hz)...", PIT.frequency());
74
75
timer::init();
76
+
77
+
// Initialize keyboard
78
+
crate::println!(" ∴ Listening for keystrokes (PS/2)...");
79
+
keyboard::init();
75
80
76
81
crate::println!(" ◈ Hardware attunement complete");
77
82
}
+248
aethelos-source/heartwood/src/loom_of_fate/context.rs
+248
aethelos-source/heartwood/src/loom_of_fate/context.rs
···
1
+
//! # Thread Context
2
+
//!
3
+
//! The essence of a thread - its complete state at a moment in time.
4
+
//! When we preserve context, we capture the thread's entire being:
5
+
//! every register, every thought, every intention.
6
+
//!
7
+
//! ## Philosophy
8
+
//! Context switches are not interruptions, but transitions.
9
+
//! We preserve the current moment with reverence, knowing we will
10
+
//! restore it faithfully when the thread's turn returns.
11
+
12
+
use core::arch::asm;
13
+
14
+
/// The complete state of a thread
15
+
///
16
+
/// This structure holds all CPU registers needed to resume execution
17
+
/// exactly where the thread left off. The layout matches x86-64 calling
18
+
/// conventions and interrupt frames.
19
+
#[repr(C)]
20
+
#[derive(Debug, Clone, Copy)]
21
+
pub struct ThreadContext {
22
+
// General purpose registers (callee-saved)
23
+
pub r15: u64,
24
+
pub r14: u64,
25
+
pub r13: u64,
26
+
pub r12: u64,
27
+
pub rbp: u64,
28
+
pub rbx: u64,
29
+
30
+
// Additional general purpose registers
31
+
pub r11: u64,
32
+
pub r10: u64,
33
+
pub r9: u64,
34
+
pub r8: u64,
35
+
pub rax: u64,
36
+
pub rcx: u64,
37
+
pub rdx: u64,
38
+
pub rsi: u64,
39
+
pub rdi: u64,
40
+
41
+
// Special registers
42
+
pub rip: u64, // Instruction pointer - where to resume
43
+
pub cs: u64, // Code segment
44
+
pub rflags: u64, // CPU flags
45
+
pub rsp: u64, // Stack pointer
46
+
pub ss: u64, // Stack segment
47
+
}
48
+
49
+
impl ThreadContext {
50
+
/// Create a new context for a thread starting at the given entry point
51
+
///
52
+
/// # Arguments
53
+
/// * `entry_point` - The function pointer where the thread will begin
54
+
/// * `stack_top` - The top of the thread's stack (high address)
55
+
///
56
+
/// # Returns
57
+
/// A context ready to be restored, which will begin executing at the entry point
58
+
pub fn new(entry_point: u64, stack_top: u64) -> Self {
59
+
ThreadContext {
60
+
// General purpose registers start at zero
61
+
r15: 0,
62
+
r14: 0,
63
+
r13: 0,
64
+
r12: 0,
65
+
rbp: 0,
66
+
rbx: 0,
67
+
r11: 0,
68
+
r10: 0,
69
+
r9: 0,
70
+
r8: 0,
71
+
rax: 0,
72
+
rcx: 0,
73
+
rdx: 0,
74
+
rsi: 0,
75
+
rdi: 0,
76
+
77
+
// Special registers
78
+
rip: entry_point,
79
+
cs: 0x08, // Kernel code segment (from GDT)
80
+
rflags: 0x202, // Interrupts enabled (IF flag)
81
+
rsp: stack_top,
82
+
ss: 0x10, // Kernel data segment (from GDT)
83
+
}
84
+
}
85
+
86
+
/// Create an empty context (all zeros)
87
+
pub const fn empty() -> Self {
88
+
ThreadContext {
89
+
r15: 0, r14: 0, r13: 0, r12: 0,
90
+
rbp: 0, rbx: 0, r11: 0, r10: 0,
91
+
r9: 0, r8: 0, rax: 0, rcx: 0,
92
+
rdx: 0, rsi: 0, rdi: 0,
93
+
rip: 0, cs: 0, rflags: 0,
94
+
rsp: 0, ss: 0,
95
+
}
96
+
}
97
+
}
98
+
99
+
/// Switch from the current thread context to a new thread context
100
+
///
101
+
/// This is the heart of cooperative multitasking. It saves the current
102
+
/// thread's state and restores the new thread's state in one atomic operation.
103
+
///
104
+
/// # Arguments
105
+
/// * `old_context` - Where to save the current thread's state
106
+
/// * `new_context` - The context to restore and resume
107
+
///
108
+
/// # Safety
109
+
/// This function performs raw register manipulation and must only be called
110
+
/// from the scheduler with interrupts disabled.
111
+
#[unsafe(naked)]
112
+
pub unsafe extern "C" fn switch_context(_old_context: *mut ThreadContext, _new_context: *const ThreadContext) {
113
+
core::arch::naked_asm!(
114
+
// Save current context
115
+
// rdi = old_context pointer (first argument)
116
+
// rsi = new_context pointer (second argument)
117
+
118
+
// Save general purpose registers
119
+
"mov [rdi + 0x00], r15",
120
+
"mov [rdi + 0x08], r14",
121
+
"mov [rdi + 0x10], r13",
122
+
"mov [rdi + 0x18], r12",
123
+
"mov [rdi + 0x20], rbp",
124
+
"mov [rdi + 0x28], rbx",
125
+
"mov [rdi + 0x30], r11",
126
+
"mov [rdi + 0x38], r10",
127
+
"mov [rdi + 0x40], r9",
128
+
"mov [rdi + 0x48], r8",
129
+
"mov [rdi + 0x50], rax",
130
+
"mov [rdi + 0x58], rcx",
131
+
"mov [rdi + 0x60], rdx",
132
+
"mov [rdi + 0x68], rsi",
133
+
"mov [rdi + 0x70], rdi",
134
+
135
+
// Save RIP (return address is on stack)
136
+
"mov rax, [rsp]",
137
+
"mov [rdi + 0x78], rax",
138
+
139
+
// Save CS
140
+
"mov ax, cs",
141
+
"mov [rdi + 0x80], rax",
142
+
143
+
// Save RFLAGS
144
+
"pushfq",
145
+
"pop rax",
146
+
"mov [rdi + 0x88], rax",
147
+
148
+
// Save RSP (before return address was pushed)
149
+
"lea rax, [rsp + 8]",
150
+
"mov [rdi + 0x90], rax",
151
+
152
+
// Save SS
153
+
"mov ax, ss",
154
+
"mov [rdi + 0x98], rax",
155
+
156
+
// Now restore new context
157
+
// rsi = new_context pointer
158
+
159
+
// Restore general purpose registers
160
+
"mov r15, [rsi + 0x00]",
161
+
"mov r14, [rsi + 0x08]",
162
+
"mov r13, [rsi + 0x10]",
163
+
"mov r12, [rsi + 0x18]",
164
+
"mov rbp, [rsi + 0x20]",
165
+
"mov rbx, [rsi + 0x28]",
166
+
"mov r11, [rsi + 0x30]",
167
+
"mov r10, [rsi + 0x38]",
168
+
"mov r9, [rsi + 0x40]",
169
+
"mov r8, [rsi + 0x48]",
170
+
"mov rax, [rsi + 0x50]",
171
+
"mov rcx, [rsi + 0x58]",
172
+
"mov rdx, [rsi + 0x60]",
173
+
174
+
// Restore RSP first (we'll need it)
175
+
"mov rsp, [rsi + 0x90]",
176
+
177
+
// Push new context's RIP onto the new stack (for ret)
178
+
"push qword ptr [rsi + 0x78]",
179
+
180
+
// Restore RFLAGS
181
+
"push qword ptr [rsi + 0x88]",
182
+
"popfq",
183
+
184
+
// Restore remaining registers
185
+
"mov rdi, [rsi + 0x70]",
186
+
"mov rsi, [rsi + 0x68]",
187
+
188
+
// Return to new thread's RIP
189
+
"ret",
190
+
);
191
+
}
192
+
193
+
/// Initialize a new thread's stack with the proper frame for first execution
194
+
///
195
+
/// This sets up the stack so that when we "restore" the context for the first time,
196
+
/// it will cleanly jump to the thread's entry point.
197
+
///
198
+
/// # Arguments
199
+
/// * `stack_top` - The top of the stack (high address)
200
+
/// * `entry_point` - The function where the thread starts
201
+
///
202
+
/// # Returns
203
+
/// The adjusted stack pointer after initialization
204
+
#[allow(dead_code)]
205
+
pub unsafe fn init_thread_stack(stack_top: u64, _entry_point: fn() -> !) -> u64 {
206
+
// The stack grows downward, so we work backward from the top
207
+
// When context is restored, ret will pop RIP from stack
208
+
// So we don't need to push anything here - the context struct handles it
209
+
210
+
stack_top
211
+
}
212
+
213
+
/// Helper wrapper for thread entry points
214
+
///
215
+
/// This function is called when a new thread is first scheduled.
216
+
/// It handles any setup needed before calling the actual entry point.
217
+
pub extern "C" fn thread_entry_wrapper(entry_point: fn() -> !) -> ! {
218
+
// Enable interrupts for this new thread
219
+
unsafe {
220
+
asm!("sti", options(nomem, nostack));
221
+
}
222
+
223
+
// Call the actual entry point
224
+
entry_point()
225
+
}
226
+
227
+
#[cfg(test)]
228
+
mod tests {
229
+
use super::*;
230
+
231
+
#[test]
232
+
fn test_context_creation() {
233
+
let ctx = ThreadContext::new(0x1000, 0x5000);
234
+
assert_eq!(ctx.rip, 0x1000);
235
+
assert_eq!(ctx.rsp, 0x5000);
236
+
assert_eq!(ctx.cs, 0x08);
237
+
assert_eq!(ctx.ss, 0x10);
238
+
assert_ne!(ctx.rflags & 0x200, 0); // IF flag set
239
+
}
240
+
241
+
#[test]
242
+
fn test_empty_context() {
243
+
let ctx = ThreadContext::empty();
244
+
assert_eq!(ctx.rip, 0);
245
+
assert_eq!(ctx.rsp, 0);
246
+
assert_eq!(ctx.rax, 0);
247
+
}
248
+
}
+3
aethelos-source/heartwood/src/loom_of_fate/mod.rs
+3
aethelos-source/heartwood/src/loom_of_fate/mod.rs
···
15
15
//! - Resource negotiation based on system-wide harmony
16
16
//! - Parasite detection and throttling (not killing)
17
17
18
+
pub mod context;
18
19
pub mod scheduler;
20
+
pub mod stack;
19
21
pub mod thread;
20
22
pub mod harmony;
21
23
···
60
62
OutOfThreads,
61
63
ThreadNotFound,
62
64
InvalidPriority,
65
+
StackAllocationFailed,
63
66
}
+119
-45
aethelos-source/heartwood/src/loom_of_fate/scheduler.rs
+119
-45
aethelos-source/heartwood/src/loom_of_fate/scheduler.rs
···
1
1
//! The Scheduler - The core of the Loom of Fate
2
2
3
+
use super::context::{switch_context, ThreadContext};
3
4
use super::harmony::{HarmonyAnalyzer, HarmonyMetrics};
5
+
use super::stack::Stack;
4
6
use super::thread::{Thread, ThreadId, ThreadPriority, ThreadState};
5
7
use super::LoomError;
6
8
use alloc::collections::VecDeque;
···
11
13
/// The harmony-based cooperative scheduler
12
14
pub struct Scheduler {
13
15
threads: Vec<Thread>,
16
+
stacks: Vec<Stack>, // Stack storage (owned by scheduler)
14
17
ready_queue: VecDeque<ThreadId>,
15
18
current_thread: Option<ThreadId>,
16
19
next_thread_id: u64,
17
20
harmony_analyzer: HarmonyAnalyzer,
18
21
/// Latest harmony metrics from the analyzer
19
22
latest_metrics: HarmonyMetrics,
23
+
/// Total number of context switches performed
24
+
context_switches: u64,
20
25
}
21
26
22
27
impl Default for Scheduler {
···
29
34
pub fn new() -> Self {
30
35
Self {
31
36
threads: Vec::new(),
37
+
stacks: Vec::new(),
32
38
ready_queue: VecDeque::new(),
33
39
current_thread: None,
34
40
next_thread_id: 1,
35
41
harmony_analyzer: HarmonyAnalyzer::new(),
36
42
latest_metrics: HarmonyMetrics::default(),
43
+
context_switches: 0,
37
44
}
38
45
}
39
46
···
46
53
let thread_id = ThreadId(self.next_thread_id);
47
54
self.next_thread_id += 1;
48
55
49
-
let thread = Thread::new(thread_id, entry_point, priority);
56
+
// Allocate a stack for this thread
57
+
let stack = Stack::new().ok_or(LoomError::StackAllocationFailed)?;
58
+
let stack_bottom = stack.bottom();
59
+
let stack_top = stack.top();
60
+
61
+
// Create the thread with its stack
62
+
let thread = Thread::new(thread_id, entry_point, priority, stack_bottom, stack_top);
63
+
50
64
self.threads.push(thread);
65
+
self.stacks.push(stack); // Keep stack alive
51
66
self.ready_queue.push_back(thread_id);
52
67
53
68
Ok(thread_id)
54
69
}
55
70
56
-
/// Yield the current thread
71
+
/// Yield the current thread and switch to the next one
72
+
///
73
+
/// This is the heart of cooperative multitasking. The current thread
74
+
/// voluntarily gives up the CPU, and we select the next thread based
75
+
/// on harmony and priority.
76
+
///
77
+
/// # Safety
78
+
/// This function performs context switching which involves raw register
79
+
/// manipulation. It should only be called from safe contexts where
80
+
/// the scheduler state is valid.
57
81
pub fn yield_current(&mut self) {
58
-
if let Some(current_id) = self.current_thread {
59
-
if let Some(thread) = self.find_thread_mut(current_id) {
60
-
thread.record_yield();
61
-
thread.set_state(ThreadState::Resting);
62
-
}
63
-
64
-
self.ready_queue.push_back(current_id);
65
-
self.current_thread = None;
66
-
}
67
-
68
-
self.schedule_next();
69
-
}
70
-
71
-
/// Schedule the next thread to run
72
-
fn schedule_next(&mut self) {
73
82
// Analyze harmony before scheduling
74
83
let metrics = self.harmony_analyzer.analyze(&mut self.threads);
75
84
self.latest_metrics = metrics;
···
77
86
// Adaptive scheduling based on system harmony
78
87
if metrics.system_harmony < 0.5 {
79
88
// System is in disharmony - prioritize cooperative threads
80
-
// and deprioritize parasites more aggressively
81
89
self.rebalance_for_harmony();
82
90
}
83
91
84
-
// Find the next thread to run based on harmony
92
+
// Find the next thread to run
85
93
let next_thread_id = self.select_next_thread();
86
94
87
-
if let Some(thread_id) = next_thread_id {
88
-
// Check if thread is parasitic and get soothe factor before mutable borrow
89
-
let (is_parasitic, soothe_factor) = self.find_thread(thread_id)
90
-
.map(|t| {
91
-
let parasitic = self.harmony_analyzer.should_soothe(t);
92
-
let factor = if parasitic {
93
-
self.harmony_analyzer.soothe_factor(t)
94
-
} else {
95
-
1.0
96
-
};
97
-
(parasitic, factor)
98
-
})
99
-
.unwrap_or((false, 1.0));
95
+
if next_thread_id.is_none() {
96
+
// No threads ready - this shouldn't happen in a well-designed system
97
+
// but if it does, we just return (stay on current thread if any)
98
+
return;
99
+
}
100
+
101
+
let next_id = next_thread_id.unwrap();
100
102
101
-
if let Some(thread) = self.find_thread_mut(thread_id) {
102
-
thread.set_state(ThreadState::Weaving);
103
-
thread.record_time_slice();
103
+
// If we're switching to a different thread, perform context switch
104
+
if self.current_thread.is_some() && self.current_thread != Some(next_id) {
105
+
let current_id = self.current_thread.unwrap();
104
106
105
-
// If this thread is parasitic, soothe it based on harmony score
106
-
if is_parasitic {
107
-
// In a real implementation, we would:
108
-
// 1. Reduce time slice based on soothe_factor (lower = more throttling)
109
-
// 2. Insert deliberate pauses/delays
110
-
// 3. Lower its effective priority
111
-
// For now, this documents the intent for future implementation
112
-
let _ = soothe_factor; // Acknowledge we have the factor
113
-
}
107
+
// Update current thread state
108
+
if let Some(current_thread) = self.find_thread_mut(current_id) {
109
+
current_thread.record_yield();
110
+
current_thread.set_state(ThreadState::Resting);
114
111
}
115
112
116
-
self.current_thread = Some(thread_id);
113
+
// Add current thread back to ready queue
114
+
self.ready_queue.push_back(current_id);
115
+
116
+
// Update next thread state
117
+
if let Some(next_thread) = self.find_thread_mut(next_id) {
118
+
next_thread.set_state(ThreadState::Weaving);
119
+
next_thread.record_time_slice();
120
+
next_thread.last_run_time = crate::attunement::timer::ticks();
121
+
}
122
+
123
+
// Perform the actual context switch
124
+
self.context_switches += 1;
125
+
self.perform_context_switch(current_id, next_id);
126
+
127
+
// After we return from context switch, we're running as the "next" thread
128
+
// (which may actually be this thread again after future switches)
129
+
} else if self.current_thread.is_none() {
130
+
// First thread to run - just start it (no context to save)
131
+
if let Some(next_thread) = self.find_thread_mut(next_id) {
132
+
next_thread.set_state(ThreadState::Weaving);
133
+
next_thread.record_time_slice();
134
+
next_thread.last_run_time = crate::attunement::timer::ticks();
135
+
}
136
+
137
+
self.current_thread = Some(next_id);
138
+
// Jump to the first thread (this will not return)
139
+
self.jump_to_thread(next_id);
117
140
}
141
+
142
+
// Update current thread ID
143
+
self.current_thread = Some(next_id);
144
+
}
145
+
146
+
/// Perform a context switch between two threads
147
+
///
148
+
/// # Safety
149
+
/// Assumes both thread IDs are valid and the threads exist
150
+
fn perform_context_switch(&mut self, from_id: ThreadId, to_id: ThreadId) {
151
+
// Get raw pointers to the contexts before borrowing
152
+
let from_idx = self.threads.iter().position(|t| t.id() == from_id).unwrap();
153
+
let to_idx = self.threads.iter().position(|t| t.id() == to_id).unwrap();
154
+
155
+
let from_ctx_ptr = &mut self.threads[from_idx].context as *mut ThreadContext;
156
+
let to_ctx_ptr = &self.threads[to_idx].context as *const ThreadContext;
157
+
158
+
// Perform the context switch
159
+
// This will save the current state to from_ctx and restore to_ctx
160
+
unsafe {
161
+
switch_context(from_ctx_ptr, to_ctx_ptr);
162
+
}
163
+
164
+
// When we return here, we're running as the "to" thread
165
+
// (or possibly some other thread that later switched back to us)
166
+
}
167
+
168
+
/// Jump to a thread for the first time (no context to save)
169
+
///
170
+
/// # Safety
171
+
/// This function never returns - it jumps to the thread's entry point
172
+
fn jump_to_thread(&mut self, to_id: ThreadId) -> ! {
173
+
// Find the thread's context
174
+
let to_idx = self.threads.iter().position(|t| t.id() == to_id).unwrap();
175
+
let to_ctx_ptr = &self.threads[to_idx].context as *const ThreadContext;
176
+
177
+
// Create a dummy context for the "from" side (we won't use it)
178
+
let mut dummy_ctx = ThreadContext::empty();
179
+
let dummy_ctx_ptr = &mut dummy_ctx as *mut ThreadContext;
180
+
181
+
// Jump to the new thread
182
+
// Note: switch_context will try to save our state to dummy_ctx,
183
+
// but since we're never coming back, that's fine
184
+
unsafe {
185
+
switch_context(dummy_ctx_ptr, to_ctx_ptr);
186
+
}
187
+
188
+
// Never reached
189
+
unreachable!("jump_to_thread should never return");
118
190
}
119
191
120
192
/// Rebalance the ready queue when system harmony is low
···
210
282
average_harmony: self.latest_metrics.average_harmony,
211
283
system_harmony: self.latest_metrics.system_harmony,
212
284
parasite_count: self.latest_metrics.parasite_count,
285
+
context_switches: self.context_switches,
213
286
}
214
287
}
215
288
···
229
302
pub average_harmony: f32,
230
303
pub system_harmony: f32,
231
304
pub parasite_count: usize,
305
+
pub context_switches: u64,
232
306
}
+148
aethelos-source/heartwood/src/loom_of_fate/stack.rs
+148
aethelos-source/heartwood/src/loom_of_fate/stack.rs
···
1
+
//! # Stack Allocation
2
+
//!
3
+
//! Each thread needs its own stack - a space for its local thoughts.
4
+
//! We allocate stacks from the heap, giving each thread room to breathe.
5
+
//!
6
+
//! ## Philosophy
7
+
//! A thread's stack is its private sanctuary. We provide generous space
8
+
//! (default 64KB) so threads never feel cramped or anxious.
9
+
10
+
use alloc::alloc::{alloc, dealloc, Layout};
11
+
use core::ptr::NonNull;
12
+
13
+
/// Default stack size: 64 KB
14
+
/// This is generous enough for most threads while being conservative with memory
15
+
pub const DEFAULT_STACK_SIZE: usize = 64 * 1024;
16
+
17
+
/// Minimum stack size: 4 KB (one page)
18
+
pub const MIN_STACK_SIZE: usize = 4 * 1024;
19
+
20
+
/// Maximum stack size: 1 MB
21
+
pub const MAX_STACK_SIZE: usize = 1024 * 1024;
22
+
23
+
/// A thread's stack allocation
24
+
pub struct Stack {
25
+
bottom: NonNull<u8>,
26
+
size: usize,
27
+
}
28
+
29
+
// Safety: Stack pointers can be safely sent between threads
30
+
// as they represent heap-allocated memory
31
+
unsafe impl Send for Stack {}
32
+
unsafe impl Sync for Stack {}
33
+
34
+
impl Stack {
35
+
/// Allocate a new stack with the default size
36
+
pub fn new() -> Option<Self> {
37
+
Self::with_size(DEFAULT_STACK_SIZE)
38
+
}
39
+
40
+
/// Allocate a new stack with a specific size
41
+
///
42
+
/// # Arguments
43
+
/// * `size` - Size in bytes (will be clamped to MIN/MAX and aligned to 16 bytes)
44
+
///
45
+
/// # Returns
46
+
/// Some(Stack) if allocation succeeds, None otherwise
47
+
pub fn with_size(size: usize) -> Option<Self> {
48
+
// Clamp size to valid range and align to 16 bytes
49
+
let size = size.clamp(MIN_STACK_SIZE, MAX_STACK_SIZE);
50
+
let size = (size + 15) & !15; // Align to 16 bytes
51
+
52
+
// Create layout for allocation
53
+
let layout = Layout::from_size_align(size, 16).ok()?;
54
+
55
+
// Allocate the stack
56
+
let ptr = unsafe { alloc(layout) };
57
+
58
+
NonNull::new(ptr).map(|bottom| Stack { bottom, size })
59
+
}
60
+
61
+
/// Get the bottom (low address) of the stack
62
+
pub fn bottom(&self) -> u64 {
63
+
self.bottom.as_ptr() as u64
64
+
}
65
+
66
+
/// Get the top (high address) of the stack
67
+
///
68
+
/// The stack grows downward, so this is bottom + size
69
+
pub fn top(&self) -> u64 {
70
+
self.bottom() + self.size as u64
71
+
}
72
+
73
+
/// Get the size of the stack in bytes
74
+
pub fn size(&self) -> usize {
75
+
self.size
76
+
}
77
+
78
+
/// Check if an address is within this stack
79
+
pub fn contains(&self, addr: u64) -> bool {
80
+
addr >= self.bottom() && addr < self.top()
81
+
}
82
+
}
83
+
84
+
impl Drop for Stack {
85
+
fn drop(&mut self) {
86
+
// Deallocate the stack when it's no longer needed
87
+
let layout = Layout::from_size_align(self.size, 16)
88
+
.expect("Invalid layout during stack deallocation");
89
+
90
+
unsafe {
91
+
dealloc(self.bottom.as_ptr(), layout);
92
+
}
93
+
}
94
+
}
95
+
96
+
/// Guard page support (for future implementation)
97
+
///
98
+
/// A guard page is a page marked as non-accessible that sits at the bottom
99
+
/// of the stack. If a stack overflow occurs, accessing the guard page will
100
+
/// trigger a page fault, allowing us to detect the overflow gracefully.
101
+
#[allow(dead_code)]
102
+
pub struct GuardedStack {
103
+
stack: Stack,
104
+
guard_page_addr: u64,
105
+
}
106
+
107
+
#[cfg(test)]
108
+
mod tests {
109
+
use super::*;
110
+
111
+
#[test]
112
+
fn test_stack_allocation() {
113
+
let stack = Stack::new().expect("Failed to allocate stack");
114
+
assert_eq!(stack.size(), DEFAULT_STACK_SIZE);
115
+
assert!(stack.top() > stack.bottom());
116
+
assert_eq!(stack.top() - stack.bottom(), DEFAULT_STACK_SIZE as u64);
117
+
}
118
+
119
+
#[test]
120
+
fn test_custom_size() {
121
+
let stack = Stack::with_size(8192).expect("Failed to allocate");
122
+
assert_eq!(stack.size(), 8192);
123
+
}
124
+
125
+
#[test]
126
+
fn test_size_clamping() {
127
+
// Too small - should clamp to MIN_STACK_SIZE
128
+
let stack = Stack::with_size(100).expect("Failed to allocate");
129
+
assert_eq!(stack.size(), MIN_STACK_SIZE);
130
+
131
+
// Too large - should clamp to MAX_STACK_SIZE
132
+
let stack = Stack::with_size(10 * 1024 * 1024).expect("Failed to allocate");
133
+
assert_eq!(stack.size(), MAX_STACK_SIZE);
134
+
}
135
+
136
+
#[test]
137
+
fn test_contains() {
138
+
let stack = Stack::new().expect("Failed to allocate");
139
+
let bottom = stack.bottom();
140
+
let top = stack.top();
141
+
142
+
assert!(stack.contains(bottom));
143
+
assert!(stack.contains(bottom + 100));
144
+
assert!(stack.contains(top - 1));
145
+
assert!(!stack.contains(top));
146
+
assert!(!stack.contains(bottom - 1));
147
+
}
148
+
}
+40
-1
aethelos-source/heartwood/src/loom_of_fate/thread.rs
+40
-1
aethelos-source/heartwood/src/loom_of_fate/thread.rs
···
1
1
//! Thread definitions - The Threads of Fate
2
2
3
+
use super::context::ThreadContext;
4
+
3
5
/// A unique identifier for a thread
4
6
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5
7
pub struct ThreadId(pub u64);
···
37
39
pub(crate) priority: ThreadPriority,
38
40
pub(crate) entry_point: fn() -> !,
39
41
42
+
// CPU state (for context switching)
43
+
pub(crate) context: ThreadContext,
44
+
pub(crate) stack_bottom: u64,
45
+
pub(crate) stack_top: u64,
46
+
40
47
// Harmony tracking
41
48
pub(crate) resource_usage: ResourceUsage,
42
49
pub(crate) harmony_score: f32,
···
44
51
// Execution context
45
52
pub(crate) time_slices_used: u64,
46
53
pub(crate) yields: u64,
54
+
pub(crate) last_run_time: u64,
47
55
}
48
56
49
57
impl Thread {
50
-
pub fn new(id: ThreadId, entry_point: fn() -> !, priority: ThreadPriority) -> Self {
58
+
/// Create a new thread with allocated stack
59
+
///
60
+
/// # Arguments
61
+
/// * `id` - Unique thread identifier
62
+
/// * `entry_point` - Function where thread begins execution
63
+
/// * `priority` - Thread priority level
64
+
/// * `stack_bottom` - Low address of thread's stack
65
+
/// * `stack_top` - High address of thread's stack
66
+
pub fn new(
67
+
id: ThreadId,
68
+
entry_point: fn() -> !,
69
+
priority: ThreadPriority,
70
+
stack_bottom: u64,
71
+
stack_top: u64,
72
+
) -> Self {
73
+
// Create initial context for this thread
74
+
let context = ThreadContext::new(entry_point as u64, stack_top);
75
+
51
76
Self {
52
77
id,
53
78
state: ThreadState::Resting,
54
79
priority,
55
80
entry_point,
81
+
context,
82
+
stack_bottom,
83
+
stack_top,
56
84
resource_usage: ResourceUsage::default(),
57
85
harmony_score: 1.0, // Start in perfect harmony
58
86
time_slices_used: 0,
59
87
yields: 0,
88
+
last_run_time: 0,
60
89
}
90
+
}
91
+
92
+
/// Get a mutable reference to the thread's context
93
+
pub fn context_mut(&mut self) -> &mut ThreadContext {
94
+
&mut self.context
95
+
}
96
+
97
+
/// Get a reference to the thread's context
98
+
pub fn context(&self) -> &ThreadContext {
99
+
&self.context
61
100
}
62
101
63
102
pub fn id(&self) -> ThreadId {