pyOS-kernel 0.1.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.
@@ -0,0 +1,180 @@
1
+ """
2
+ pyOS Assembler - Assembly to Machine Code
3
+ """
4
+
5
+ import subprocess
6
+ import tempfile
7
+ import os
8
+ from typing import Optional
9
+ from pathlib import Path
10
+
11
+
12
+ class AssemblerError(Exception):
13
+ """Raised when assembly fails."""
14
+ pass
15
+
16
+
17
+ class Assembler:
18
+ """
19
+ Assembles x86/x86_64 Assembly code to machine code.
20
+
21
+ Uses NASM (Netwide Assembler) for assembly.
22
+ """
23
+
24
+ def __init__(self, arch: str = "x86"):
25
+ """
26
+ Initialize assembler.
27
+
28
+ Args:
29
+ arch: Target architecture ("x86" or "x86_64")
30
+ """
31
+ self.arch = arch
32
+ self.nasm_path = self._find_nasm()
33
+
34
+ def _find_nasm(self) -> str:
35
+ """Find NASM executable."""
36
+ # Try common locations
37
+ paths = [
38
+ "nasm",
39
+ "/usr/bin/nasm",
40
+ "/usr/local/bin/nasm",
41
+ "C:\\Program Files\\NASM\\nasm.exe",
42
+ "C:\\NASM\\nasm.exe",
43
+ ]
44
+
45
+ for path in paths:
46
+ try:
47
+ result = subprocess.run(
48
+ [path, "-v"],
49
+ capture_output=True,
50
+ text=True,
51
+ )
52
+ if result.returncode == 0:
53
+ return path
54
+ except FileNotFoundError:
55
+ continue
56
+
57
+ return "nasm" # Hope it's in PATH
58
+
59
+ def assemble(self, asm_code: str, output_format: str = "bin") -> bytes:
60
+ """
61
+ Assemble code to machine code.
62
+
63
+ Args:
64
+ asm_code: Assembly source code
65
+ output_format: Output format (bin, elf, elf64)
66
+
67
+ Returns:
68
+ Binary machine code.
69
+
70
+ Raises:
71
+ AssemblerError: If assembly fails.
72
+ """
73
+ with tempfile.TemporaryDirectory() as tmpdir:
74
+ asm_file = Path(tmpdir) / "kernel.asm"
75
+ out_file = Path(tmpdir) / "kernel.bin"
76
+
77
+ # Write assembly to file
78
+ asm_file.write_text(asm_code)
79
+
80
+ # Determine format
81
+ if self.arch == "x86_64":
82
+ fmt = "elf64" if output_format == "elf" else output_format
83
+ else:
84
+ fmt = "elf32" if output_format == "elf" else output_format
85
+
86
+ # Run NASM
87
+ cmd = [
88
+ self.nasm_path,
89
+ "-f", fmt,
90
+ "-o", str(out_file),
91
+ str(asm_file),
92
+ ]
93
+
94
+ result = subprocess.run(
95
+ cmd,
96
+ capture_output=True,
97
+ text=True,
98
+ )
99
+
100
+ if result.returncode != 0:
101
+ raise AssemblerError(f"NASM failed:\n{result.stderr}")
102
+
103
+ # Read binary output
104
+ return out_file.read_bytes()
105
+
106
+ def assemble_to_file(self, asm_code: str, output_path: str, output_format: str = "bin") -> str:
107
+ """
108
+ Assemble code and write to file.
109
+
110
+ Args:
111
+ asm_code: Assembly source code
112
+ output_path: Output file path
113
+ output_format: Output format
114
+
115
+ Returns:
116
+ Path to output file.
117
+ """
118
+ binary = self.assemble(asm_code, output_format)
119
+
120
+ with open(output_path, "wb") as f:
121
+ f.write(binary)
122
+
123
+ return output_path
124
+
125
+ def link(self, object_files: list, output_path: str) -> str:
126
+ """
127
+ Link object files into executable.
128
+
129
+ Args:
130
+ object_files: List of object file paths
131
+ output_path: Output executable path
132
+
133
+ Returns:
134
+ Path to linked executable.
135
+ """
136
+ # Use ld for linking
137
+ cmd = ["ld", "-m"]
138
+
139
+ if self.arch == "x86_64":
140
+ cmd.append("elf_x86_64")
141
+ else:
142
+ cmd.append("elf_i386")
143
+
144
+ cmd.extend(["-T", "linker.ld"])
145
+ cmd.extend(["-o", output_path])
146
+ cmd.extend(object_files)
147
+
148
+ result = subprocess.run(cmd, capture_output=True, text=True)
149
+
150
+ if result.returncode != 0:
151
+ raise AssemblerError(f"Linker failed:\n{result.stderr}")
152
+
153
+ return output_path
154
+
155
+ @staticmethod
156
+ def is_nasm_available() -> bool:
157
+ """Check if NASM is available."""
158
+ try:
159
+ result = subprocess.run(
160
+ ["nasm", "-v"],
161
+ capture_output=True,
162
+ )
163
+ return result.returncode == 0
164
+ except FileNotFoundError:
165
+ return False
166
+
167
+ @staticmethod
168
+ def get_nasm_version() -> Optional[str]:
169
+ """Get NASM version string."""
170
+ try:
171
+ result = subprocess.run(
172
+ ["nasm", "-v"],
173
+ capture_output=True,
174
+ text=True,
175
+ )
176
+ if result.returncode == 0:
177
+ return result.stdout.strip()
178
+ except FileNotFoundError:
179
+ pass
180
+ return None
@@ -0,0 +1,238 @@
1
+ """
2
+ pyOS Code Generator - Generates Assembly from Python
3
+ """
4
+
5
+ from typing import TYPE_CHECKING, List, Dict, Any
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
+
11
+ if TYPE_CHECKING:
12
+ from ..kernel import Kernel
13
+
14
+
15
+ class CodeGenerator:
16
+ """
17
+ Generates x86/x86_64 Assembly code from Python kernel code.
18
+ """
19
+
20
+ def __init__(self, kernel: 'Kernel'):
21
+ self.kernel = kernel
22
+ self.arch = kernel.config.arch.value
23
+ self.asm_lines: List[str] = []
24
+ self.string_counter = 0
25
+ self.label_counter = 0
26
+ self._strings_to_emit = []
27
+ self._func_operations = {} # Operations per function
28
+
29
+ def generate(self) -> str:
30
+ """Generate complete Assembly code."""
31
+ self.asm_lines = []
32
+ self.string_counter = 0
33
+ self._strings_to_emit = []
34
+ self._func_operations = {}
35
+
36
+ # Execute each boot function separately and capture its operations
37
+ for boot_func in self.kernel._boot_functions:
38
+ Screen._reset()
39
+ boot_func.func()
40
+ ops = Screen._get_operations().copy()
41
+ self._func_operations[boot_func.name] = ops
42
+
43
+ # Collect strings from this function
44
+ for op in ops:
45
+ if op["type"] == "print" and "text" in op:
46
+ label = f"str_{self.string_counter}"
47
+ self.string_counter += 1
48
+ text = op["text"]
49
+ self._strings_to_emit.append((label, text))
50
+ op["_string_label"] = label
51
+
52
+ # Header
53
+ self._emit_header()
54
+
55
+ # Text section
56
+ self._emit_text_section()
57
+
58
+ return '\n'.join(self.asm_lines)
59
+
60
+ def _emit(self, line: str) -> None:
61
+ """Emit a line of assembly."""
62
+ self.asm_lines.append(line)
63
+
64
+ def _emit_header(self) -> None:
65
+ """Emit assembly header."""
66
+ self._emit("; pyOS Generated Kernel")
67
+ self._emit(f"; Architecture: {self.arch}")
68
+ self._emit("; Generated by pyOS Compiler")
69
+ self._emit("")
70
+
71
+ if self.arch == "x86":
72
+ self._emit("[BITS 32]")
73
+ else:
74
+ self._emit("[BITS 64]")
75
+
76
+ self._emit("[ORG 0x1000]")
77
+ self._emit("")
78
+
79
+ def _emit_text_section(self) -> None:
80
+ """Emit text section with code."""
81
+ self._emit("; ==================")
82
+ self._emit("; Kernel Entry Point")
83
+ self._emit("; ==================")
84
+ self._emit("")
85
+ self._emit("_start:")
86
+ self._emit(" mov esp, 0x90000")
87
+
88
+ # Call boot functions in correct order (sorted by priority, lower first)
89
+ sorted_funcs = sorted(self.kernel._boot_functions, key=lambda x: x.priority)
90
+
91
+ self._emit("")
92
+ self._emit(" ; Call boot functions")
93
+ for boot_func in sorted_funcs:
94
+ self._emit(f" call {boot_func.name}")
95
+
96
+ # Main loop
97
+ self._emit("")
98
+ self._emit(" ; Main loop - halt CPU")
99
+ self._emit(".main_loop:")
100
+ self._emit(" hlt")
101
+ self._emit(" jmp .main_loop")
102
+
103
+ # Generate boot functions
104
+ self._emit("")
105
+ self._generate_boot_functions()
106
+
107
+ # Generate helper functions
108
+ self._emit("")
109
+ self._generate_screen_functions()
110
+ self._generate_keyboard_functions()
111
+
112
+ def _generate_boot_functions(self) -> None:
113
+ """Generate code for boot functions."""
114
+ for boot_func in self.kernel._boot_functions:
115
+ self._emit(f"; Function: {boot_func.name}")
116
+ self._emit(f"{boot_func.name}:")
117
+ self._emit(" push ebp")
118
+ self._emit(" mov ebp, esp")
119
+
120
+ # Get operations for THIS function only
121
+ ops = self._func_operations.get(boot_func.name, [])
122
+
123
+ # Generate code for screen operations
124
+ for op in ops:
125
+ if op["type"] == "clear":
126
+ self._emit(" call screen_clear")
127
+ elif op["type"] == "print":
128
+ label = op.get("_string_label", "str_0")
129
+ row = op.get("row", 0)
130
+ col = op.get("col", 0)
131
+ fg = op.get("foreground", 15)
132
+ bg = op.get("background", 0)
133
+ color = (bg << 4) | fg
134
+ self._emit(f" push dword {color}")
135
+ self._emit(f" push dword {col}")
136
+ self._emit(f" push dword {row}")
137
+ self._emit(f" push dword {label}")
138
+ self._emit(" call screen_print")
139
+ self._emit(" add esp, 16")
140
+
141
+ self._emit(" pop ebp")
142
+ self._emit(" ret")
143
+ self._emit("")
144
+
145
+ def _generate_screen_functions(self) -> None:
146
+ """Generate VGA screen functions."""
147
+ self._emit("; ==================")
148
+ self._emit("; Screen Functions")
149
+ self._emit("; ==================")
150
+ self._emit("VGA_MEMORY equ 0xB8000")
151
+ self._emit("VGA_WIDTH equ 80")
152
+ self._emit("VGA_HEIGHT equ 25")
153
+ self._emit("")
154
+
155
+ # screen_clear
156
+ self._emit("screen_clear:")
157
+ self._emit(" push ebp")
158
+ self._emit(" mov ebp, esp")
159
+ self._emit(" push edi")
160
+ self._emit(" push eax")
161
+ self._emit(" push ecx")
162
+ self._emit("")
163
+ self._emit(" mov edi, VGA_MEMORY")
164
+ self._emit(" mov ecx, VGA_WIDTH * VGA_HEIGHT")
165
+ self._emit(" mov ax, 0x0720")
166
+ self._emit(" rep stosw")
167
+ self._emit("")
168
+ self._emit(" pop ecx")
169
+ self._emit(" pop eax")
170
+ self._emit(" pop edi")
171
+ self._emit(" pop ebp")
172
+ self._emit(" ret")
173
+ self._emit("")
174
+
175
+ # screen_print (string, row, col, color)
176
+ self._emit("screen_print:")
177
+ self._emit(" push ebp")
178
+ self._emit(" mov ebp, esp")
179
+ self._emit(" push esi")
180
+ self._emit(" push edi")
181
+ self._emit(" push eax")
182
+ self._emit(" push ebx")
183
+ self._emit("")
184
+ self._emit(" mov esi, [ebp+8]")
185
+ self._emit(" mov eax, [ebp+12]")
186
+ self._emit(" mov ebx, VGA_WIDTH * 2")
187
+ self._emit(" mul ebx")
188
+ self._emit(" mov edi, [ebp+16]")
189
+ self._emit(" shl edi, 1")
190
+ self._emit(" add edi, eax")
191
+ self._emit(" add edi, VGA_MEMORY")
192
+ self._emit(" mov ah, [ebp+20]")
193
+ self._emit("")
194
+ self._emit(".sp_loop:")
195
+ self._emit(" lodsb")
196
+ self._emit(" test al, al")
197
+ self._emit(" jz .sp_done")
198
+ self._emit(" stosw")
199
+ self._emit(" jmp .sp_loop")
200
+ self._emit("")
201
+ self._emit(".sp_done:")
202
+ self._emit(" pop ebx")
203
+ self._emit(" pop eax")
204
+ self._emit(" pop edi")
205
+ self._emit(" pop esi")
206
+ self._emit(" pop ebp")
207
+ self._emit(" ret")
208
+ self._emit("")
209
+
210
+ def _generate_keyboard_functions(self) -> None:
211
+ """Generate keyboard functions."""
212
+ self._emit("; ==================")
213
+ self._emit("; Keyboard Functions")
214
+ self._emit("; ==================")
215
+ self._emit("KEYBOARD_DATA_PORT equ 0x60")
216
+ self._emit("KEYBOARD_STATUS_PORT equ 0x64")
217
+ self._emit("")
218
+
219
+ self._emit("keyboard_read_scancode:")
220
+ self._emit(" push ebp")
221
+ self._emit(" mov ebp, esp")
222
+ self._emit(".kb_wait:")
223
+ self._emit(" in al, KEYBOARD_STATUS_PORT")
224
+ self._emit(" test al, 1")
225
+ self._emit(" jz .kb_wait")
226
+ self._emit(" in al, KEYBOARD_DATA_PORT")
227
+ self._emit(" movzx eax, al")
228
+ self._emit(" pop ebp")
229
+ self._emit(" ret")
230
+ self._emit("")
231
+
232
+ # Emit strings at the end
233
+ self._emit("; ==================")
234
+ self._emit("; Data Section")
235
+ self._emit("; ==================")
236
+ for label, text in self._strings_to_emit:
237
+ escaped = text.replace('\\', '\\\\').replace('"', '\\"')
238
+ self._emit(f'{label}: db "{escaped}", 0')
@@ -0,0 +1,8 @@
1
+ """
2
+ pyOS Hardware Drivers
3
+ """
4
+
5
+ from .screen import Screen
6
+ from .keyboard import Keyboard
7
+
8
+ __all__ = ["Screen", "Keyboard"]