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 +22 -0
- pyos/boot/__init__.py +3 -0
- pyos/boot/bootloader.asm +174 -0
- pyos/builder.py +177 -0
- pyos/cli.py +286 -0
- pyos/compiler/__init__.py +8 -0
- pyos/compiler/assembler.py +180 -0
- pyos/compiler/codegen.py +238 -0
- pyos/drivers/__init__.py +8 -0
- pyos/drivers/keyboard.py +319 -0
- pyos/drivers/screen.py +310 -0
- pyos/emulator.py +195 -0
- pyos/interrupts/__init__.py +8 -0
- pyos/interrupts/handler.py +237 -0
- pyos/interrupts/idt.py +165 -0
- pyos/kernel.py +288 -0
- pyos/memory/__init__.py +8 -0
- pyos/memory/gdt.py +295 -0
- pyos/memory/manager.py +397 -0
- pyos/syscalls/__init__.py +7 -0
- pyos/syscalls/handler.py +179 -0
- pyos_kernel-0.1.0.dist-info/METADATA +323 -0
- pyos_kernel-0.1.0.dist-info/RECORD +27 -0
- pyos_kernel-0.1.0.dist-info/WHEEL +5 -0
- pyos_kernel-0.1.0.dist-info/entry_points.txt +2 -0
- pyos_kernel-0.1.0.dist-info/licenses/LICENSE +21 -0
- pyos_kernel-0.1.0.dist-info/top_level.txt +1 -0
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
pyos/boot/bootloader.asm
ADDED
|
@@ -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()
|