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.
pyos/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ """
2
+ pyOS - Build Operating Systems with Python
3
+ """
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
12
+
13
+ __version__ = "0.1.0"
14
+ __all__ = [
15
+ "Kernel",
16
+ "Screen",
17
+ "Keyboard",
18
+ "Memory",
19
+ "GDT",
20
+ "Interrupts",
21
+ "SysCall",
22
+ ]
pyos/boot/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """
2
+ pyOS Boot Components
3
+ """
@@ -0,0 +1,174 @@
1
+ ; pyOS Bootloader
2
+ ; Simple bootloader that loads kernel and switches to protected mode
3
+
4
+ [BITS 16]
5
+ [ORG 0x7C00]
6
+
7
+ KERNEL_OFFSET equ 0x1000
8
+
9
+ start:
10
+ ; Setup segments
11
+ xor ax, ax
12
+ mov ds, ax
13
+ mov es, ax
14
+ mov ss, ax
15
+ mov sp, 0x7C00
16
+
17
+ ; Save boot drive
18
+ mov [BOOT_DRIVE], dl
19
+
20
+ ; Print message
21
+ mov si, MSG_BOOT
22
+ call print
23
+
24
+ ; Load kernel
25
+ call load_kernel
26
+
27
+ mov si, MSG_OK
28
+ call print
29
+
30
+ ; Go to protected mode
31
+ cli
32
+
33
+ ; Enable A20
34
+ in al, 0x92
35
+ or al, 2
36
+ out 0x92, al
37
+
38
+ ; Load GDT
39
+ lgdt [gdt_desc]
40
+
41
+ ; Enter protected mode
42
+ mov eax, cr0
43
+ or eax, 1
44
+ mov cr0, eax
45
+
46
+ jmp 0x08:pm_start
47
+
48
+ ;----------------------------------------
49
+ ; Print (SI = string)
50
+ ;----------------------------------------
51
+ print:
52
+ pusha
53
+ .loop:
54
+ lodsb
55
+ test al, al
56
+ jz .done
57
+ mov ah, 0x0E
58
+ int 0x10
59
+ jmp .loop
60
+ .done:
61
+ popa
62
+ ret
63
+
64
+ ;----------------------------------------
65
+ ; Load kernel - read sectors one by one
66
+ ;----------------------------------------
67
+ load_kernel:
68
+ pusha
69
+
70
+ mov si, MSG_LOAD
71
+ call print
72
+
73
+ ; Read 16 sectors (8KB) - enough for our kernel
74
+ mov bx, KERNEL_OFFSET ; Destination
75
+ mov cl, 2 ; Start sector
76
+ mov ch, 0 ; Cylinder
77
+ mov dh, 0 ; Head
78
+ mov dl, [BOOT_DRIVE]
79
+
80
+ mov al, 16 ; Sectors to read
81
+
82
+ .read:
83
+ mov ah, 0x02 ; Read function
84
+ mov al, 1 ; Read 1 sector at a time
85
+ int 0x13
86
+ jc .error
87
+
88
+ add bx, 512 ; Next buffer position
89
+ inc cl ; Next sector
90
+ cmp cl, 18 ; Sector 18 reached?
91
+ jne .continue
92
+ mov cl, 1 ; Reset to sector 1
93
+ inc dh ; Next head
94
+ cmp dh, 2
95
+ jne .continue
96
+ mov dh, 0
97
+ inc ch ; Next cylinder
98
+
99
+ .continue:
100
+ cmp bx, KERNEL_OFFSET + (16 * 512) ; Read 16 sectors?
101
+ jb .read
102
+
103
+ popa
104
+ ret
105
+
106
+ .error:
107
+ mov si, MSG_ERR
108
+ call print
109
+ jmp $
110
+
111
+ ;----------------------------------------
112
+ ; GDT
113
+ ;----------------------------------------
114
+ gdt_start:
115
+ dq 0 ; Null descriptor
116
+
117
+ gdt_code:
118
+ dw 0xFFFF ; Limit
119
+ dw 0x0000 ; Base low
120
+ db 0x00 ; Base mid
121
+ db 10011010b ; Access
122
+ db 11001111b ; Flags + limit high
123
+ db 0x00 ; Base high
124
+
125
+ gdt_data:
126
+ dw 0xFFFF
127
+ dw 0x0000
128
+ db 0x00
129
+ db 10010010b
130
+ db 11001111b
131
+ db 0x00
132
+ gdt_end:
133
+
134
+ gdt_desc:
135
+ dw gdt_end - gdt_start - 1
136
+ dd gdt_start
137
+
138
+ ;----------------------------------------
139
+ ; 32-bit mode
140
+ ;----------------------------------------
141
+ [BITS 32]
142
+ pm_start:
143
+ mov ax, 0x10 ; Data segment
144
+ mov ds, ax
145
+ mov es, ax
146
+ mov fs, ax
147
+ mov gs, ax
148
+ mov ss, ax
149
+ mov esp, 0x90000
150
+
151
+ ; Jump to kernel
152
+ call KERNEL_OFFSET
153
+
154
+ ; Halt
155
+ cli
156
+ .halt:
157
+ hlt
158
+ jmp .halt
159
+
160
+ ;----------------------------------------
161
+ ; Data
162
+ ;----------------------------------------
163
+ [BITS 16]
164
+ BOOT_DRIVE: db 0
165
+ MSG_BOOT: db "pyOS v0.1", 13, 10, 0
166
+ MSG_LOAD: db "Loading...", 13, 10, 0
167
+ MSG_OK: db "OK", 13, 10, 0
168
+ MSG_ERR: db "Disk Error!", 0
169
+
170
+ ;----------------------------------------
171
+ ; Boot signature
172
+ ;----------------------------------------
173
+ times 510 - ($ - $$) db 0
174
+ dw 0xAA55
pyos/builder.py ADDED
@@ -0,0 +1,177 @@
1
+ """
2
+ pyOS Builder - Build OS images
3
+ """
4
+
5
+ import subprocess
6
+ import tempfile
7
+ import shutil
8
+ import os
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING, Optional
11
+
12
+ if TYPE_CHECKING:
13
+ from .kernel import Kernel
14
+
15
+
16
+ class BuildError(Exception):
17
+ """Raised when build fails."""
18
+ pass
19
+
20
+
21
+ class OSBuilder:
22
+ """
23
+ Builds complete OS images from compiled kernel.
24
+ """
25
+
26
+ def __init__(self, kernel: 'Kernel'):
27
+ self.kernel = kernel
28
+ self.bootloader_path = Path(__file__).parent / "boot" / "bootloader.asm"
29
+
30
+ def build_bin(self, output_path: str) -> str:
31
+ """
32
+ Build a raw binary image.
33
+
34
+ Args:
35
+ output_path: Output file path
36
+
37
+ Returns:
38
+ Path to the built image.
39
+ """
40
+ from .compiler.assembler import Assembler
41
+ from .compiler.codegen import CodeGenerator
42
+
43
+ with tempfile.TemporaryDirectory() as tmpdir:
44
+ tmpdir = Path(tmpdir)
45
+
46
+ # Assemble bootloader
47
+ assembler = Assembler(self.kernel.config.arch.value)
48
+ bootloader_bin = tmpdir / "bootloader.bin"
49
+
50
+ bootloader_asm = self.bootloader_path.read_text()
51
+ assembler.assemble_to_file(bootloader_asm, str(bootloader_bin), "bin")
52
+
53
+ # Generate and assemble kernel
54
+ generator = CodeGenerator(self.kernel)
55
+ kernel_asm = generator.generate()
56
+
57
+ kernel_bin = tmpdir / "kernel.bin"
58
+ assembler.assemble_to_file(kernel_asm, str(kernel_bin), "bin")
59
+
60
+ # Combine bootloader + kernel
61
+ with open(output_path, "wb") as out:
62
+ # Write bootloader (512 bytes)
63
+ bootloader_data = bootloader_bin.read_bytes()
64
+ out.write(bootloader_data)
65
+
66
+ # Write kernel
67
+ kernel_data = kernel_bin.read_bytes()
68
+ out.write(kernel_data)
69
+
70
+ # Pad to 16 sectors (8KB) after bootloader
71
+ current_size = len(bootloader_data) + len(kernel_data)
72
+ target_size = 512 + (16 * 512) # Bootloader + 16 sectors
73
+ if current_size < target_size:
74
+ out.write(bytes(target_size - current_size))
75
+
76
+ return output_path
77
+
78
+ def build_iso(self, output_path: str) -> str:
79
+ """
80
+ Build an ISO image for CD/USB boot.
81
+
82
+ Args:
83
+ output_path: Output file path
84
+
85
+ Returns:
86
+ Path to the built ISO.
87
+ """
88
+ with tempfile.TemporaryDirectory() as tmpdir:
89
+ tmpdir = Path(tmpdir)
90
+
91
+ # Build binary first
92
+ bin_path = tmpdir / "os.bin"
93
+ self.build_bin(str(bin_path))
94
+
95
+ # Create ISO directory structure
96
+ iso_dir = tmpdir / "iso"
97
+ boot_dir = iso_dir / "boot"
98
+ grub_dir = boot_dir / "grub"
99
+ grub_dir.mkdir(parents=True)
100
+
101
+ # Copy kernel
102
+ shutil.copy(bin_path, boot_dir / "kernel.bin")
103
+
104
+ # Create GRUB config
105
+ grub_cfg = grub_dir / "grub.cfg"
106
+ grub_cfg.write_text("""
107
+ set timeout=0
108
+ set default=0
109
+
110
+ menuentry "pyOS" {
111
+ multiboot /boot/kernel.bin
112
+ boot
113
+ }
114
+ """)
115
+
116
+ # Try to create ISO with grub-mkrescue
117
+ if self._has_grub_mkrescue():
118
+ self._create_iso_grub(iso_dir, output_path)
119
+ else:
120
+ # Fallback: just copy the binary
121
+ shutil.copy(bin_path, output_path)
122
+ print("Warning: grub-mkrescue not found, created raw binary instead of ISO")
123
+
124
+ return output_path
125
+
126
+ def _has_grub_mkrescue(self) -> bool:
127
+ """Check if grub-mkrescue is available."""
128
+ try:
129
+ result = subprocess.run(
130
+ ["grub-mkrescue", "--version"],
131
+ capture_output=True,
132
+ )
133
+ return result.returncode == 0
134
+ except FileNotFoundError:
135
+ return False
136
+
137
+ def _create_iso_grub(self, iso_dir: Path, output_path: str) -> None:
138
+ """Create ISO using grub-mkrescue."""
139
+ cmd = [
140
+ "grub-mkrescue",
141
+ "-o", output_path,
142
+ str(iso_dir),
143
+ ]
144
+
145
+ result = subprocess.run(cmd, capture_output=True, text=True)
146
+
147
+ if result.returncode != 0:
148
+ raise BuildError(f"grub-mkrescue failed:\n{result.stderr}")
149
+
150
+ def get_asm(self) -> str:
151
+ """
152
+ Get the generated Assembly code.
153
+
154
+ Returns:
155
+ Assembly source code.
156
+ """
157
+ from .compiler.codegen import CodeGenerator
158
+
159
+ generator = CodeGenerator(self.kernel)
160
+ return generator.generate()
161
+
162
+ def save_asm(self, output_path: str) -> str:
163
+ """
164
+ Save generated Assembly to file.
165
+
166
+ Args:
167
+ output_path: Output file path
168
+
169
+ Returns:
170
+ Path to saved file.
171
+ """
172
+ asm = self.get_asm()
173
+
174
+ with open(output_path, "w") as f:
175
+ f.write(asm)
176
+
177
+ return output_path
pyos/cli.py ADDED
@@ -0,0 +1,286 @@
1
+ """
2
+ pyOS Command Line Interface
3
+ """
4
+
5
+ import click
6
+ import sys
7
+ from pathlib import Path
8
+
9
+
10
+ @click.group()
11
+ @click.version_option(version="0.1.0", prog_name="pyOS")
12
+ def main():
13
+ """
14
+ pyOS - Build Operating Systems with Python
15
+
16
+ Write your OS in Python, compile to Assembly and Machine Code.
17
+ """
18
+ pass
19
+
20
+
21
+ @main.command()
22
+ @click.argument("source", type=click.Path(exists=True))
23
+ @click.option("-o", "--output", default="os.iso", help="Output file path")
24
+ @click.option("-f", "--format", "fmt", type=click.Choice(["iso", "bin"]), default="iso", help="Output format")
25
+ @click.option("-a", "--arch", type=click.Choice(["x86", "x86_64"]), default="x86", help="Target architecture")
26
+ @click.option("-v", "--verbose", is_flag=True, help="Verbose output")
27
+ def build(source: str, output: str, fmt: str, arch: str, verbose: bool):
28
+ """
29
+ Build OS from Python source file.
30
+
31
+ Example:
32
+ pyos build main.py -o myos.iso
33
+ """
34
+ click.echo(f"Building {source} -> {output}")
35
+
36
+ try:
37
+ # Import and execute the source file
38
+ source_path = Path(source)
39
+
40
+ if verbose:
41
+ click.echo(f" Architecture: {arch}")
42
+ click.echo(f" Format: {fmt}")
43
+
44
+ # Read and execute the Python file
45
+ code = source_path.read_text()
46
+
47
+ # Create a namespace for execution
48
+ namespace = {}
49
+ exec(code, namespace)
50
+
51
+ # Find the kernel object
52
+ kernel = None
53
+ for name, obj in namespace.items():
54
+ if hasattr(obj, 'build') and hasattr(obj, '_boot_functions'):
55
+ kernel = obj
56
+ break
57
+
58
+ if kernel is None:
59
+ click.echo("Error: No Kernel object found in source file", err=True)
60
+ sys.exit(1)
61
+
62
+ # Build
63
+ result = kernel.build(output, format=fmt)
64
+
65
+ click.echo(f"✓ Built successfully: {result}")
66
+
67
+ except Exception as e:
68
+ click.echo(f"Error: {e}", err=True)
69
+ if verbose:
70
+ import traceback
71
+ traceback.print_exc()
72
+ sys.exit(1)
73
+
74
+
75
+ @main.command()
76
+ @click.argument("image", type=click.Path(exists=True))
77
+ @click.option("-m", "--memory", default=128, help="Memory in MB")
78
+ @click.option("-d", "--debug", is_flag=True, help="Enable debug mode (GDB)")
79
+ def run(image: str, memory: int, debug: bool):
80
+ """
81
+ Run OS image in QEMU.
82
+
83
+ Example:
84
+ pyos run myos.iso
85
+ pyos run myos.bin --debug
86
+ """
87
+ from .emulator import QEMURunner, QEMUError
88
+
89
+ click.echo(f"Running {image} in QEMU...")
90
+
91
+ if debug:
92
+ click.echo("Debug mode enabled. Connect GDB to localhost:1234")
93
+
94
+ try:
95
+ runner = QEMURunner()
96
+ process = runner.run(image, memory=memory, debug=debug)
97
+
98
+ click.echo("QEMU started. Close the window to exit.")
99
+ process.wait()
100
+
101
+ except QEMUError as e:
102
+ click.echo(f"Error: {e}", err=True)
103
+ sys.exit(1)
104
+
105
+
106
+ @main.command()
107
+ @click.argument("image", type=click.Path(exists=True))
108
+ @click.option("-m", "--memory", default=128, help="Memory in MB")
109
+ def debug(image: str, memory: int):
110
+ """
111
+ Run OS in debug mode with GDB server.
112
+
113
+ Example:
114
+ pyos debug myos.iso
115
+
116
+ Then connect with: gdb -ex "target remote localhost:1234"
117
+ """
118
+ from .emulator import QEMURunner, QEMUError
119
+
120
+ click.echo(f"Starting {image} in debug mode...")
121
+ click.echo("GDB server listening on localhost:1234")
122
+ click.echo("Connect with: gdb -ex 'target remote localhost:1234'")
123
+ click.echo("")
124
+
125
+ try:
126
+ runner = QEMURunner()
127
+ process = runner.run(image, memory=memory, debug=True)
128
+ process.wait()
129
+
130
+ except QEMUError as e:
131
+ click.echo(f"Error: {e}", err=True)
132
+ sys.exit(1)
133
+
134
+
135
+ @main.command()
136
+ @click.argument("source", type=click.Path(exists=True))
137
+ @click.option("-o", "--output", default="output.asm", help="Output file path")
138
+ def asm(source: str, output: str):
139
+ """
140
+ Generate Assembly code from Python source.
141
+
142
+ Example:
143
+ pyos asm main.py -o kernel.asm
144
+ """
145
+ click.echo(f"Generating Assembly: {source} -> {output}")
146
+
147
+ try:
148
+ source_path = Path(source)
149
+ code = source_path.read_text()
150
+
151
+ namespace = {}
152
+ exec(code, namespace)
153
+
154
+ kernel = None
155
+ for name, obj in namespace.items():
156
+ if hasattr(obj, 'compile') and hasattr(obj, '_boot_functions'):
157
+ kernel = obj
158
+ break
159
+
160
+ if kernel is None:
161
+ click.echo("Error: No Kernel object found", err=True)
162
+ sys.exit(1)
163
+
164
+ asm_code = kernel.compile()
165
+
166
+ with open(output, "w") as f:
167
+ f.write(asm_code)
168
+
169
+ click.echo(f"✓ Assembly generated: {output}")
170
+
171
+ except Exception as e:
172
+ click.echo(f"Error: {e}", err=True)
173
+ sys.exit(1)
174
+
175
+
176
+ @main.command()
177
+ def check():
178
+ """
179
+ Check if required tools are installed.
180
+
181
+ Example:
182
+ pyos check
183
+ """
184
+ from .compiler.assembler import Assembler
185
+ from .emulator import QEMURunner
186
+
187
+ click.echo("Checking required tools...")
188
+ click.echo("")
189
+
190
+ # Check NASM
191
+ if Assembler.is_nasm_available():
192
+ version = Assembler.get_nasm_version()
193
+ click.echo(f"✓ NASM: {version}")
194
+ else:
195
+ click.echo("✗ NASM: Not found")
196
+ click.echo(" Install: https://www.nasm.us/")
197
+
198
+ # Check QEMU
199
+ if QEMURunner.is_available():
200
+ version = QEMURunner.get_version()
201
+ click.echo(f"✓ QEMU: {version}")
202
+ else:
203
+ click.echo("✗ QEMU: Not found")
204
+ click.echo(" Install: https://www.qemu.org/download/")
205
+
206
+ click.echo("")
207
+
208
+
209
+ @main.command()
210
+ @click.argument("name", default="myos")
211
+ def new(name: str):
212
+ """
213
+ Create a new pyOS project.
214
+
215
+ Example:
216
+ pyos new myos
217
+ """
218
+ project_dir = Path(name)
219
+
220
+ if project_dir.exists():
221
+ click.echo(f"Error: Directory '{name}' already exists", err=True)
222
+ sys.exit(1)
223
+
224
+ project_dir.mkdir()
225
+
226
+ # Create main.py
227
+ main_py = project_dir / "main.py"
228
+ main_py.write_text('''"""
229
+ My Operating System built with pyOS
230
+ """
231
+
232
+ from pyos import Kernel, Screen
233
+
234
+ # Create kernel
235
+ kernel = Kernel(arch="x86")
236
+
237
+ @kernel.on_boot
238
+ def main():
239
+ """Main boot function."""
240
+ Screen.clear()
241
+ Screen.set_color("green", "black")
242
+ Screen.print("Welcome to My OS!")
243
+ Screen.print("Built with pyOS", row=2)
244
+ Screen.set_color("white", "black")
245
+ Screen.print("Press any key...", row=4)
246
+
247
+ # Build the OS
248
+ if __name__ == "__main__":
249
+ kernel.build("myos.iso")
250
+ ''')
251
+
252
+ # Create README
253
+ readme = project_dir / "README.md"
254
+ readme.write_text(f'''# {name}
255
+
256
+ An operating system built with pyOS.
257
+
258
+ ## Build
259
+
260
+ ```bash
261
+ pyos build main.py -o {name}.iso
262
+ ```
263
+
264
+ ## Run
265
+
266
+ ```bash
267
+ pyos run {name}.iso
268
+ ```
269
+
270
+ ## Debug
271
+
272
+ ```bash
273
+ pyos debug {name}.iso
274
+ ```
275
+ ''')
276
+
277
+ click.echo(f"✓ Created new pyOS project: {name}/")
278
+ click.echo("")
279
+ click.echo("Next steps:")
280
+ click.echo(f" cd {name}")
281
+ click.echo(f" pyos build main.py -o {name}.iso")
282
+ click.echo(f" pyos run {name}.iso")
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
@@ -0,0 +1,8 @@
1
+ """
2
+ pyOS Compiler - Python to Assembly
3
+ """
4
+
5
+ from .codegen import CodeGenerator
6
+ from .assembler import Assembler
7
+
8
+ __all__ = ["CodeGenerator", "Assembler"]