pyOS-kernel 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. pyos/__init__.py +40 -12
  2. pyos/api/__init__.py +38 -0
  3. pyos/api/errors.py +35 -0
  4. pyos/api/fs.py +46 -0
  5. pyos/{memory → api}/gdt.py +8 -7
  6. pyos/api/idt.py +1 -0
  7. pyos/api/interrupts.py +97 -0
  8. pyos/api/kernel.py +271 -0
  9. pyos/api/keyboard.py +136 -0
  10. pyos/api/memory.py +178 -0
  11. pyos/api/process.py +42 -0
  12. pyos/api/screen.py +235 -0
  13. pyos/api/syscalls.py +82 -0
  14. pyos/boot/bootloader.asm +41 -18
  15. pyos/build/__init__.py +7 -0
  16. pyos/{builder.py → build/builder.py} +91 -86
  17. pyos/build/codegen.py +337 -0
  18. pyos/cli.py +5 -5
  19. pyos/emulator.py +1 -1
  20. pyos/kernel/__init__.py +1 -0
  21. pyos/kernel/arch/x86/gdt.c +79 -0
  22. pyos/{runtime/src → kernel/arch/x86}/idt.c +2 -1
  23. pyos/kernel/arch/x86/paging.c +45 -0
  24. pyos/{runtime/src → kernel/arch/x86}/start.S +4 -0
  25. pyos/kernel/arch/x86/syscall.c +84 -0
  26. pyos/kernel/arch/x86/usercopy.c +32 -0
  27. pyos/kernel/drivers/floppy.c +19 -0
  28. pyos/{runtime/src → kernel/drivers}/keyboard.c +37 -3
  29. pyos/kernel/drivers/pit.c +54 -0
  30. pyos/{runtime/src → kernel/drivers}/screen.c +34 -0
  31. pyos/kernel/drivers/shell.c +103 -0
  32. pyos/kernel/fs/fat12.c +34 -0
  33. pyos/kernel/fs/ramfs.c +2 -0
  34. pyos/kernel/fs/vfs.c +108 -0
  35. pyos/kernel/include/elf.h +8 -0
  36. pyos/kernel/include/fat12.h +9 -0
  37. pyos/kernel/include/floppy.h +12 -0
  38. pyos/kernel/include/gdt.h +9 -0
  39. pyos/{runtime → kernel}/include/heap.h +4 -0
  40. pyos/{runtime → kernel}/include/kernel.h +10 -2
  41. pyos/{runtime → kernel}/include/keyboard.h +3 -1
  42. pyos/kernel/include/paging.h +11 -0
  43. pyos/{runtime → kernel}/include/pic.h +2 -0
  44. pyos/kernel/include/pmm.h +10 -0
  45. pyos/{runtime → kernel}/include/screen.h +6 -2
  46. pyos/kernel/include/shell.h +11 -0
  47. pyos/kernel/include/string.h +12 -0
  48. pyos/kernel/include/syscall.h +22 -0
  49. pyos/kernel/include/task.h +35 -0
  50. pyos/kernel/include/timer.h +12 -0
  51. pyos/kernel/include/usercopy.h +10 -0
  52. pyos/kernel/include/vfs.h +26 -0
  53. pyos/kernel/kmain.c +78 -0
  54. pyos/{runtime/src → kernel/lib}/debug.c +3 -1
  55. pyos/kernel/lib/string.c +34 -0
  56. pyos/kernel/mm/heap.c +110 -0
  57. pyos/kernel/mm/pmm.c +40 -0
  58. pyos/kernel/proc/elf.c +11 -0
  59. pyos/kernel/proc/task.c +90 -0
  60. pyos_kernel-1.0.0.dist-info/METADATA +474 -0
  61. pyos_kernel-1.0.0.dist-info/RECORD +78 -0
  62. pyos_kernel-1.0.0.dist-info/licenses/LICENSE +21 -0
  63. pyos/compiler/__init__.py +0 -8
  64. pyos/compiler/codegen.py +0 -204
  65. pyos/drivers/__init__.py +0 -8
  66. pyos/drivers/keyboard.py +0 -319
  67. pyos/drivers/screen.py +0 -310
  68. pyos/interrupts/__init__.py +0 -8
  69. pyos/interrupts/handler.py +0 -237
  70. pyos/interrupts/idt.py +0 -165
  71. pyos/kernel.py +0 -295
  72. pyos/memory/__init__.py +0 -8
  73. pyos/memory/manager.py +0 -397
  74. pyos/runtime/include/syscall.h +0 -13
  75. pyos/runtime/src/heap.c +0 -48
  76. pyos/runtime/src/kmain.c +0 -50
  77. pyos/runtime/src/syscall.c +0 -43
  78. pyos/syscalls/__init__.py +0 -7
  79. pyos/syscalls/handler.py +0 -179
  80. pyos_kernel-0.2.0.dist-info/METADATA +0 -143
  81. pyos_kernel-0.2.0.dist-info/RECORD +0 -51
  82. /pyos/{compiler → build}/assembler.py +0 -0
  83. /pyos/{toolchain.py → build/toolchain.py} +0 -0
  84. /pyos/{runtime/src → kernel/arch/x86}/isr.S +0 -0
  85. /pyos/{runtime/src → kernel/arch/x86}/isr.asm +0 -0
  86. /pyos/{runtime/src → kernel/arch/x86}/pic.c +0 -0
  87. /pyos/{runtime/src → kernel/arch/x86}/start.asm +0 -0
  88. /pyos/{runtime → kernel}/include/debug.h +0 -0
  89. /pyos/{runtime → kernel}/include/idt.h +0 -0
  90. /pyos/{runtime → kernel}/include/io.h +0 -0
  91. /pyos/{runtime → kernel}/include/types.h +0 -0
  92. /pyos/{runtime → kernel}/linker.ld +0 -0
  93. {pyos_kernel-0.2.0.dist-info → pyos_kernel-1.0.0.dist-info}/WHEEL +0 -0
  94. {pyos_kernel-0.2.0.dist-info → pyos_kernel-1.0.0.dist-info}/entry_points.txt +0 -0
  95. {pyos_kernel-0.2.0.dist-info → pyos_kernel-1.0.0.dist-info}/top_level.txt +0 -0
pyos/__init__.py CHANGED
@@ -1,22 +1,50 @@
1
1
  """
2
- pyOS - Build Operating Systems with Python
2
+ pyOS - Build real operating systems with a Python DSL
3
3
  """
4
4
 
5
- from .kernel import Kernel
6
- from .drivers.screen import Screen
7
- from .drivers.keyboard import Keyboard
8
- from .memory.manager import Memory
9
- from .memory.gdt import GDT
10
- from .interrupts.handler import Interrupts
11
- from .syscalls.handler import SysCall
5
+ from .api import (
6
+ GDT,
7
+ Architecture,
8
+ BuildTimeOnlyError,
9
+ COLOR_NAMES,
10
+ CapabilityError,
11
+ Color,
12
+ InterruptType,
13
+ Interrupts,
14
+ Kernel,
15
+ KernelConfig,
16
+ KeyCode,
17
+ KeyEvent,
18
+ Keyboard,
19
+ Memory,
20
+ Screen,
21
+ SysCall,
22
+ SysCallNumber,
23
+ UnsupportedOpError,
24
+ )
25
+ from .api.fs import File
26
+ from .api.process import Process
12
27
 
13
- __version__ = "0.2.0"
28
+ __version__ = "1.0.0"
14
29
  __all__ = [
30
+ "Architecture",
31
+ "BuildTimeOnlyError",
32
+ "COLOR_NAMES",
33
+ "CapabilityError",
34
+ "Color",
35
+ "File",
36
+ "GDT",
37
+ "InterruptType",
38
+ "Interrupts",
15
39
  "Kernel",
16
- "Screen",
40
+ "KernelConfig",
41
+ "KeyCode",
42
+ "KeyEvent",
17
43
  "Keyboard",
18
44
  "Memory",
19
- "GDT",
20
- "Interrupts",
45
+ "Process",
46
+ "Screen",
21
47
  "SysCall",
48
+ "SysCallNumber",
49
+ "UnsupportedOpError",
22
50
  ]
pyos/api/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """Public Python DSL for building pyOS kernels."""
2
+
3
+ from .errors import BuildTimeOnlyError, CapabilityError, UnsupportedOpError
4
+ from .gdt import GDT, GDTEntry, PrivilegeLevel, SegmentType
5
+ from .interrupts import InterruptFrame, InterruptType, Interrupts
6
+ from .kernel import Architecture, Kernel, KernelConfig
7
+ from .keyboard import KeyCode, KeyEvent, Keyboard
8
+ from .memory import Memory, MemoryBlock, MemoryRegion
9
+ from .screen import COLOR_NAMES, Color, Screen
10
+ from .syscalls import SysCall, SysCallContext, SysCallNumber
11
+
12
+ __all__ = [
13
+ "Architecture",
14
+ "BuildTimeOnlyError",
15
+ "COLOR_NAMES",
16
+ "CapabilityError",
17
+ "Color",
18
+ "GDT",
19
+ "GDTEntry",
20
+ "InterruptFrame",
21
+ "InterruptType",
22
+ "Interrupts",
23
+ "Kernel",
24
+ "KernelConfig",
25
+ "KeyCode",
26
+ "KeyEvent",
27
+ "Keyboard",
28
+ "Memory",
29
+ "MemoryBlock",
30
+ "MemoryRegion",
31
+ "PrivilegeLevel",
32
+ "Screen",
33
+ "SegmentType",
34
+ "SysCall",
35
+ "SysCallContext",
36
+ "SysCallNumber",
37
+ "UnsupportedOpError",
38
+ ]
pyos/api/errors.py ADDED
@@ -0,0 +1,35 @@
1
+ """Honest API errors for pyOS — no silent stubs."""
2
+
3
+
4
+ class CapabilityError(Exception):
5
+ """Raised when a Python API feature is not available in the current kernel."""
6
+
7
+ def __init__(self, capability: str, hint: str = ""):
8
+ self.capability = capability
9
+ msg = f"Capability '{capability}' is not available in this pyOS build."
10
+ if hint:
11
+ msg = f"{msg}\n Hint: {hint}"
12
+ super().__init__(msg)
13
+
14
+
15
+ class BuildTimeOnlyError(Exception):
16
+ """Raised when runtime Python logic is expected but only build-time recording exists."""
17
+
18
+ def __init__(self, api: str, hint: str = ""):
19
+ msg = (
20
+ f"'{api}' cannot run arbitrary Python inside the C kernel. "
21
+ "Only recorded build-time operations are emitted to C glue."
22
+ )
23
+ if hint:
24
+ msg = f"{msg}\n Hint: {hint}"
25
+ super().__init__(msg)
26
+
27
+
28
+ class UnsupportedOpError(Exception):
29
+ """Raised at codegen time when an operation cannot be emitted."""
30
+
31
+ def __init__(self, op: str, hint: str = ""):
32
+ msg = f"Unsupported kernel operation at build time: {op}"
33
+ if hint:
34
+ msg = f"{msg}\n Hint: {hint}"
35
+ super().__init__(msg)
pyos/api/fs.py ADDED
@@ -0,0 +1,46 @@
1
+ """Filesystem API — available when Kernel capability 'filesystem' is enabled."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, List, Union
6
+
7
+ from .errors import CapabilityError
8
+
9
+
10
+ class File:
11
+ """Seed files into the OS image at build time; runtime uses syscalls."""
12
+
13
+ _seed_files: Dict[str, bytes] = {}
14
+ _kernel_ref = None
15
+
16
+ @classmethod
17
+ def bind_kernel(cls, kernel) -> None:
18
+ cls._kernel_ref = kernel
19
+
20
+ @classmethod
21
+ def _require(cls) -> None:
22
+ k = cls._kernel_ref
23
+ if k is None or not k.capabilities.get("filesystem"):
24
+ raise CapabilityError(
25
+ "filesystem",
26
+ "Enable with Kernel(enable_filesystem=True) once Phase 5 runtime is linked",
27
+ )
28
+
29
+ @classmethod
30
+ def write_at_build(cls, path: str, data: Union[bytes, str]) -> None:
31
+ cls._require()
32
+ if isinstance(data, str):
33
+ data = data.encode("ascii", errors="replace")
34
+ cls._seed_files[path.lstrip("/")] = data
35
+
36
+ @classmethod
37
+ def _reset(cls) -> None:
38
+ cls._seed_files = {}
39
+
40
+ @classmethod
41
+ def _get_seeds(cls) -> Dict[str, bytes]:
42
+ return dict(cls._seed_files)
43
+
44
+ @classmethod
45
+ def list_seeds(cls) -> List[str]:
46
+ return list(cls._seed_files.keys())
@@ -235,14 +235,15 @@ class GDT:
235
235
 
236
236
  def install(self) -> None:
237
237
  """
238
- Install the GDT and reload segment registers.
239
-
240
- This must be called after adding all segments.
241
-
242
- Example:
243
- gdt.install()
238
+ GDT is installed by the C kernel when enable_user_mode/enable_paging is set.
239
+ Calling this from Python documents intent only.
244
240
  """
245
- self._operations.append({"type": "install"})
241
+ from .errors import UnsupportedOpError
242
+
243
+ raise UnsupportedOpError(
244
+ "GDT.install",
245
+ "Pass Kernel(enable_user_mode=True) or enable_paging=True — C gdt_init() runs at boot",
246
+ )
246
247
 
247
248
  def get_selector(self, index: int, ring: int = 0) -> int:
248
249
  """
pyos/api/idt.py ADDED
@@ -0,0 +1 @@
1
+ # pyOS package
pyos/api/interrupts.py ADDED
@@ -0,0 +1,97 @@
1
+ """
2
+ pyOS Interrupt API — honest: enable/disable/mask emit to C; custom handlers unsupported.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass
8
+ from enum import Enum
9
+ from typing import Callable, Dict, Optional
10
+
11
+ from .errors import UnsupportedOpError
12
+
13
+
14
+ class InterruptType(Enum):
15
+ DIVIDE_ERROR = 0
16
+ IRQ_TIMER = 32
17
+ IRQ_KEYBOARD = 33
18
+ SYSCALL = 0x80
19
+
20
+
21
+ @dataclass
22
+ class InterruptFrame:
23
+ eip: int = 0
24
+ cs: int = 0
25
+ eflags: int = 0
26
+ esp: int = 0
27
+ ss: int = 0
28
+ error_code: int = 0
29
+ interrupt_number: int = 0
30
+
31
+
32
+ class Interrupts:
33
+ _handlers: Dict[int, Callable] = {}
34
+ _enabled: bool = False
35
+ _operations: list = []
36
+
37
+ @classmethod
38
+ def handler(cls, interrupt: InterruptType):
39
+ def decorator(func: Callable) -> Callable:
40
+ raise UnsupportedOpError(
41
+ f"Interrupts.handler({interrupt.name})",
42
+ "Use @kernel.on_keypress / @kernel.on_timer; IDT is owned by the C kernel",
43
+ )
44
+
45
+ return decorator
46
+
47
+ @classmethod
48
+ def register(cls, interrupt_number: int, handler: Callable) -> None:
49
+ raise UnsupportedOpError("Interrupts.register")
50
+
51
+ @classmethod
52
+ def unregister(cls, interrupt_number: int) -> None:
53
+ raise UnsupportedOpError("Interrupts.unregister")
54
+
55
+ @classmethod
56
+ def enable(cls) -> None:
57
+ cls._enabled = True
58
+ cls._operations.append({"type": "irq_enable"})
59
+
60
+ @classmethod
61
+ def disable(cls) -> None:
62
+ cls._enabled = False
63
+ cls._operations.append({"type": "irq_disable"})
64
+
65
+ @classmethod
66
+ def is_enabled(cls) -> bool:
67
+ return cls._enabled
68
+
69
+ @classmethod
70
+ def send_eoi(cls, irq: int) -> None:
71
+ cls._operations.append({"type": "irq_eoi", "irq": int(irq)})
72
+
73
+ @classmethod
74
+ def mask_irq(cls, irq: int) -> None:
75
+ cls._operations.append({"type": "irq_mask", "irq": int(irq)})
76
+
77
+ @classmethod
78
+ def unmask_irq(cls, irq: int) -> None:
79
+ cls._operations.append({"type": "irq_unmask", "irq": int(irq)})
80
+
81
+ @classmethod
82
+ def trigger_software_interrupt(cls, interrupt_number: int) -> None:
83
+ raise UnsupportedOpError("Interrupts.trigger_software_interrupt")
84
+
85
+ @classmethod
86
+ def get_handler(cls, interrupt_number: int) -> Optional[Callable]:
87
+ return None
88
+
89
+ @classmethod
90
+ def _reset(cls) -> None:
91
+ cls._handlers = {}
92
+ cls._enabled = False
93
+ cls._operations = []
94
+
95
+ @classmethod
96
+ def _get_operations(cls) -> list:
97
+ return cls._operations.copy()
pyos/api/kernel.py ADDED
@@ -0,0 +1,271 @@
1
+ """
2
+ pyOS Kernel — Python DSL that builds a real freestanding C kernel.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum
9
+ from typing import Any, Callable, Dict, List, Optional
10
+
11
+ from .errors import CapabilityError, UnsupportedOpError
12
+
13
+
14
+ class Architecture(Enum):
15
+ X86 = "x86"
16
+ X86_64 = "x86_64"
17
+
18
+
19
+ @dataclass
20
+ class KernelConfig:
21
+ arch: Architecture = Architecture.X86
22
+ stack_size: int = 16384
23
+ heap_size: int = 1048576
24
+ heap_start: int = 0x200000
25
+ stack_top: int = 0x90000
26
+ video_mode: str = "text"
27
+ enable_interrupts: bool = True
28
+ enable_gdt: bool = True
29
+ enable_paging: bool = False
30
+ enable_user_mode: bool = False
31
+ enable_processes: bool = False
32
+ enable_filesystem: bool = False
33
+ debug_level: str = "lab" # quiet | lab
34
+ keypress_mode: str = "echo" # echo | custom
35
+
36
+
37
+ @dataclass
38
+ class KernelFunction:
39
+ name: str
40
+ func: Callable
41
+ event: str
42
+ priority: int = 0
43
+ key_filter: Optional[str] = None
44
+ interval_ms: int = 1000
45
+
46
+
47
+ # Active kernel for Memory/Screen/Process/File binding during boot recording
48
+ _ACTIVE_KERNEL: Optional["Kernel"] = None
49
+
50
+
51
+ def get_active_kernel() -> Optional["Kernel"]:
52
+ return _ACTIVE_KERNEL
53
+
54
+
55
+ class Kernel:
56
+ """
57
+ Build-time OS definition. Boot handlers run on the host to record ops;
58
+ the C kernel executes the generated glue at runtime.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ arch: str = "x86",
64
+ stack_size: int = 16384,
65
+ heap_size: int = 1048576,
66
+ enable_interrupts: bool = True,
67
+ enable_gdt: bool = True,
68
+ enable_paging: bool = False,
69
+ enable_user_mode: bool = False,
70
+ enable_processes: bool = False,
71
+ enable_filesystem: bool = False,
72
+ debug_level: str = "lab",
73
+ keypress_mode: str = "echo",
74
+ ):
75
+ if arch != "x86":
76
+ raise ValueError(
77
+ "Only arch='x86' is supported.\n"
78
+ " Why: the C kernel and bootloader are 32-bit protected mode.\n"
79
+ " Hint: use Kernel(arch='x86')"
80
+ )
81
+ if debug_level not in ("quiet", "lab"):
82
+ raise ValueError("debug_level must be 'quiet' or 'lab'")
83
+ if keypress_mode not in ("echo", "custom"):
84
+ raise ValueError("keypress_mode must be 'echo' or 'custom'")
85
+
86
+ # stack grows down from stack_top; keep classic low-memory top
87
+ stack_top = 0x90000
88
+ if stack_size < 4096 or stack_size > 0x80000:
89
+ raise ValueError("stack_size must be between 4096 and 524288")
90
+
91
+ self.config = KernelConfig(
92
+ arch=Architecture(arch),
93
+ stack_size=stack_size,
94
+ heap_size=heap_size,
95
+ stack_top=stack_top,
96
+ enable_interrupts=enable_interrupts,
97
+ enable_gdt=enable_gdt,
98
+ enable_paging=enable_paging,
99
+ enable_user_mode=enable_user_mode,
100
+ enable_processes=enable_processes,
101
+ enable_filesystem=enable_filesystem,
102
+ debug_level=debug_level,
103
+ keypress_mode=keypress_mode,
104
+ )
105
+
106
+ self.capabilities: Dict[str, bool] = {
107
+ "screen_text": True,
108
+ "heap_alloc": True,
109
+ "heap_free": True,
110
+ "keyboard_irq": True,
111
+ "timer": True,
112
+ "syscalls": True,
113
+ "paging": enable_paging,
114
+ "user_mode": enable_user_mode,
115
+ "processes": enable_processes,
116
+ "filesystem": enable_filesystem,
117
+ "gdt_runtime": enable_gdt,
118
+ }
119
+
120
+ self._boot_functions: List[KernelFunction] = []
121
+ self._interrupt_handlers: Dict[int, KernelFunction] = {}
122
+ self._syscall_handlers: Dict[int, KernelFunction] = {}
123
+ self._keypress_handlers: List[KernelFunction] = []
124
+ self._timer_handlers: List[KernelFunction] = []
125
+ self._custom_handlers: Dict[str, List[KernelFunction]] = {}
126
+ self._compiled_asm: Optional[str] = None
127
+ self._compiled_binary: Optional[bytes] = None
128
+ self._seed_files: Dict[str, bytes] = {}
129
+
130
+ from .fs import File
131
+ from .memory import Memory
132
+ from .process import Process
133
+
134
+ Memory.bind_kernel(self)
135
+ Process.bind_kernel(self)
136
+ File.bind_kernel(self)
137
+
138
+ def require_capability(self, name: str, hint: str = "") -> None:
139
+ if not self.capabilities.get(name):
140
+ raise CapabilityError(name, hint or f"Pass the matching Kernel(...) flag to enable '{name}'")
141
+
142
+ def on_boot(self, func: Callable = None, *, priority: int = 0):
143
+ def decorator(f: Callable) -> Callable:
144
+ self._boot_functions.append(
145
+ KernelFunction(name=f.__name__, func=f, event="boot", priority=priority)
146
+ )
147
+ self._boot_functions.sort(key=lambda x: x.priority)
148
+ return f
149
+
150
+ if func is not None:
151
+ return decorator(func)
152
+ return decorator
153
+
154
+ def on_keypress(self, func: Callable = None, *, key: str = None, mode: str = None):
155
+ def decorator(f: Callable) -> Callable:
156
+ handler = KernelFunction(name=f.__name__, func=f, event="keypress")
157
+ handler.key_filter = key
158
+ self._keypress_handlers.append(handler)
159
+ if mode:
160
+ self.config.keypress_mode = mode
161
+ return f
162
+
163
+ if func is not None:
164
+ return decorator(func)
165
+ return decorator
166
+
167
+ def on_interrupt(self, interrupt_number: int):
168
+ def decorator(func: Callable) -> Callable:
169
+ raise UnsupportedOpError(
170
+ f"on_interrupt({interrupt_number})",
171
+ "Custom IRQ handlers are reserved; use @on_keypress / @on_timer / built-in IDT",
172
+ )
173
+
174
+ return decorator
175
+
176
+ def on_syscall(self, syscall_number: int):
177
+ def decorator(func: Callable) -> Callable:
178
+ raise UnsupportedOpError(
179
+ f"on_syscall({syscall_number})",
180
+ "Syscall table is fixed in C (see SysCallNumber). Extend the C dispatcher to add numbers.",
181
+ )
182
+
183
+ return decorator
184
+
185
+ def on_timer(self, interval_ms: int = 1000):
186
+ def decorator(func: Callable) -> Callable:
187
+ self.require_capability("timer", "Timer requires enable_interrupts=True")
188
+ if not self.config.enable_interrupts:
189
+ raise CapabilityError("timer", "Set enable_interrupts=True")
190
+ handler = KernelFunction(name=func.__name__, func=func, event="timer")
191
+ handler.interval_ms = max(10, int(interval_ms))
192
+ self._timer_handlers.append(handler)
193
+ return func
194
+
195
+ return decorator
196
+
197
+ def on_event(self, event_name: str):
198
+ def decorator(func: Callable) -> Callable:
199
+ raise UnsupportedOpError(
200
+ f"on_event({event_name!r})",
201
+ "Custom event bus is not part of the C kernel; use on_boot/on_timer/on_keypress",
202
+ )
203
+
204
+ return decorator
205
+
206
+ def seed_file(self, path: str, data) -> None:
207
+ self.require_capability("filesystem")
208
+ if isinstance(data, str):
209
+ data = data.encode("ascii", errors="replace")
210
+ self._seed_files[path.lstrip("/")] = data
211
+
212
+ def compile(self) -> str:
213
+ global _ACTIVE_KERNEL
214
+ from ..build.codegen import CodeGenerator
215
+
216
+ _ACTIVE_KERNEL = self
217
+ try:
218
+ generator = CodeGenerator(self)
219
+ self._compiled_asm = generator.generate()
220
+ return self._compiled_asm
221
+ finally:
222
+ _ACTIVE_KERNEL = None
223
+
224
+ def assemble(self) -> bytes:
225
+ from pathlib import Path
226
+ import tempfile
227
+
228
+ from ..build.builder import OSBuilder
229
+
230
+ builder = OSBuilder(self)
231
+ with tempfile.TemporaryDirectory() as tmp:
232
+ path = Path(tmp) / "kernel.bin"
233
+ builder.build_bin(str(path))
234
+ self._compiled_binary = path.read_bytes()
235
+ return self._compiled_binary
236
+
237
+ def build(self, output: str, format: str = "bin") -> str:
238
+ from ..build.builder import OSBuilder
239
+
240
+ builder = OSBuilder(self)
241
+ if format == "iso":
242
+ return builder.build_iso(output)
243
+ if format == "bin":
244
+ return builder.build_bin(output)
245
+ raise ValueError(f"Unknown format: {format}")
246
+
247
+ def run(self, image_path: str = None, debug: bool = False):
248
+ from ..emulator import QEMURunner
249
+
250
+ if image_path is None:
251
+ image_path = self.build("temp_os.bin")
252
+ runner = QEMURunner(self.config.arch)
253
+ runner.run(image_path, debug=debug)
254
+
255
+ def get_info(self) -> Dict[str, Any]:
256
+ return {
257
+ "architecture": self.config.arch.value,
258
+ "stack_size": self.config.stack_size,
259
+ "stack_top": self.config.stack_top,
260
+ "heap_size": self.config.heap_size,
261
+ "interrupts_enabled": self.config.enable_interrupts,
262
+ "gdt_enabled": self.config.enable_gdt,
263
+ "paging": self.config.enable_paging,
264
+ "user_mode": self.config.enable_user_mode,
265
+ "processes": self.config.enable_processes,
266
+ "filesystem": self.config.enable_filesystem,
267
+ "boot_functions": len(self._boot_functions),
268
+ "keypress_handlers": len(self._keypress_handlers),
269
+ "timer_handlers": len(self._timer_handlers),
270
+ "capabilities": dict(self.capabilities),
271
+ }