osmk 0.1.0__tar.gz
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.
- osmk-0.1.0/PKG-INFO +242 -0
- osmk-0.1.0/README.md +225 -0
- osmk-0.1.0/osmk/__init__.py +619 -0
- osmk-0.1.0/osmk/engine.py +261 -0
- osmk-0.1.0/osmk/registers.py +85 -0
- osmk-0.1.0/osmk.egg-info/PKG-INFO +242 -0
- osmk-0.1.0/osmk.egg-info/SOURCES.txt +11 -0
- osmk-0.1.0/osmk.egg-info/dependency_links.txt +1 -0
- osmk-0.1.0/osmk.egg-info/requires.txt +3 -0
- osmk-0.1.0/osmk.egg-info/top_level.txt +1 -0
- osmk-0.1.0/pyproject.toml +28 -0
- osmk-0.1.0/setup.cfg +4 -0
- osmk-0.1.0/tests/test_osmk.py +282 -0
osmk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: osmk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Write x86 operating systems in Python — one instruction at a time
|
|
5
|
+
Author-email: Your Name <you@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: os,assembly,x86,bootloader,osdev,low-level
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Education
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: System :: Operating System Kernels
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
17
|
+
|
|
18
|
+
# osmk
|
|
19
|
+
|
|
20
|
+
**Write x86 operating systems in Python — one instruction at a time.**
|
|
21
|
+
|
|
22
|
+
Every function call maps to an x86 assembly instruction. `build()` assembles it into a bootable 512-byte disk image.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install osmk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
You also need **NASM** (the assembler) and optionally **QEMU** (to run your OS):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Ubuntu/Debian
|
|
34
|
+
sudo apt install nasm qemu-system-x86
|
|
35
|
+
|
|
36
|
+
# macOS
|
|
37
|
+
brew install nasm qemu
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import osmk
|
|
44
|
+
from osmk import ax, bx
|
|
45
|
+
|
|
46
|
+
osmk.start("hello.img") # begin a new OS
|
|
47
|
+
|
|
48
|
+
osmk.ax(5) # mov ax, 5
|
|
49
|
+
osmk.bx(5) # mov bx, 5
|
|
50
|
+
osmk.add(ax, bx) # add ax, bx → ax = 0x000A
|
|
51
|
+
osmk.writehex() # print AX as hex → "000A"
|
|
52
|
+
|
|
53
|
+
osmk.build() # assemble → hello.img (512 bytes)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run it:
|
|
57
|
+
```bash
|
|
58
|
+
qemu-system-i386 -drive format=raw,file=hello.img
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API Reference
|
|
62
|
+
|
|
63
|
+
### Lifecycle
|
|
64
|
+
|
|
65
|
+
| Function | Description |
|
|
66
|
+
|----------|-------------|
|
|
67
|
+
| `start(filename, bits=16, org=0x7C00)` | Begin a new OS program |
|
|
68
|
+
| `build()` | Assemble and create the bootable image |
|
|
69
|
+
| `dump_asm()` | Return the generated NASM source (for debugging) |
|
|
70
|
+
| `run(memory=128)` | Build and launch in QEMU |
|
|
71
|
+
|
|
72
|
+
### Register Setters
|
|
73
|
+
|
|
74
|
+
Every register is callable. `osmk.ax(5)` → `mov ax, 5`.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
osmk.ax(5) # mov ax, 5
|
|
78
|
+
osmk.bx(ax) # mov bx, ax
|
|
79
|
+
osmk.al(0x41) # mov al, 0x41
|
|
80
|
+
osmk.eax(0x1000) # mov eax, 0x1000
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Available: `ax bx cx dx si di sp bp` · `al ah bl bh cl ch dl dh` · `eax ebx ecx edx esi edi esp ebp`
|
|
84
|
+
|
|
85
|
+
### Instructions
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Arithmetic
|
|
89
|
+
osmk.add(ax, bx) osmk.sub(ax, 1)
|
|
90
|
+
osmk.mul(bx) osmk.div(cx)
|
|
91
|
+
osmk.inc(ax) osmk.dec(cx)
|
|
92
|
+
osmk.neg(ax)
|
|
93
|
+
|
|
94
|
+
# Bitwise
|
|
95
|
+
osmk.and_(ax, 0xFF) osmk.or_(ax, bx)
|
|
96
|
+
osmk.xor(ax, ax) osmk.not_(ax)
|
|
97
|
+
osmk.shl(ax, 4) osmk.shr(ax, 1)
|
|
98
|
+
|
|
99
|
+
# Compare & Jump
|
|
100
|
+
osmk.cmp(ax, 0)
|
|
101
|
+
osmk.je("done") osmk.jne("loop")
|
|
102
|
+
osmk.jl("less") osmk.jg("greater")
|
|
103
|
+
osmk.jz("zero") osmk.jnz("nonzero")
|
|
104
|
+
osmk.jmp("start") osmk.jc("carry")
|
|
105
|
+
|
|
106
|
+
# Stack
|
|
107
|
+
osmk.push(ax) osmk.pop(bx)
|
|
108
|
+
|
|
109
|
+
# Subroutines
|
|
110
|
+
osmk.call("myfunc") osmk.ret()
|
|
111
|
+
|
|
112
|
+
# Interrupts
|
|
113
|
+
osmk.interrupt(0x10) # int 0x10
|
|
114
|
+
osmk.cli() osmk.sti()
|
|
115
|
+
osmk.hlt()
|
|
116
|
+
|
|
117
|
+
# Labels
|
|
118
|
+
osmk.label("loop")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Output Helpers
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
osmk.writehex() # print AX as 4-digit hex (e.g. "000A")
|
|
125
|
+
osmk.writechar("A") # print character 'A'
|
|
126
|
+
osmk.writechar() # print whatever's in AL
|
|
127
|
+
osmk.prints("Hello!") # print a string
|
|
128
|
+
osmk.newline() # print CR+LF
|
|
129
|
+
osmk.readchar() # wait for keypress → AL
|
|
130
|
+
osmk.halt() # halt CPU (cli + hlt loop)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Custom / Escape Hatch
|
|
134
|
+
|
|
135
|
+
When osmk doesn't cover what you need:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# Inject raw NASM assembly
|
|
139
|
+
osmk.asm("mov ah, 0x0E")
|
|
140
|
+
osmk.asm("""
|
|
141
|
+
mov ah, 0x00
|
|
142
|
+
mov al, 0x03
|
|
143
|
+
int 0x10
|
|
144
|
+
""")
|
|
145
|
+
|
|
146
|
+
# Inject raw bytes
|
|
147
|
+
osmk.raw(b"\x90\x90\x90") # 3x NOP
|
|
148
|
+
|
|
149
|
+
# Define data
|
|
150
|
+
osmk.data("my_var", "dw 0x1234")
|
|
151
|
+
|
|
152
|
+
# Define a subroutine
|
|
153
|
+
with osmk.function("beep") as fn:
|
|
154
|
+
fn.asm("mov ah, 0x0E")
|
|
155
|
+
fn.asm("mov al, 0x07") # BEL character
|
|
156
|
+
fn.asm("int 0x10")
|
|
157
|
+
|
|
158
|
+
osmk.call("beep")
|
|
159
|
+
|
|
160
|
+
# Add comments
|
|
161
|
+
osmk.comment("this sets up video mode")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Explicit MOV
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
osmk.mov(ax, 0x0E) # same as osmk.ax(0x0E)
|
|
168
|
+
osmk.mov(bx, ax) # same as osmk.bx(ax)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Examples
|
|
172
|
+
|
|
173
|
+
### Hello World
|
|
174
|
+
```python
|
|
175
|
+
import osmk
|
|
176
|
+
|
|
177
|
+
osmk.start("hello.img")
|
|
178
|
+
osmk.prints("Hello, World!")
|
|
179
|
+
osmk.newline()
|
|
180
|
+
osmk.prints("My first OS!")
|
|
181
|
+
osmk.halt()
|
|
182
|
+
osmk.build()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Keyboard Echo
|
|
186
|
+
```python
|
|
187
|
+
import osmk
|
|
188
|
+
from osmk import cx
|
|
189
|
+
|
|
190
|
+
osmk.start("echo.img")
|
|
191
|
+
osmk.prints("Type anything:")
|
|
192
|
+
osmk.newline()
|
|
193
|
+
|
|
194
|
+
osmk.label("loop")
|
|
195
|
+
osmk.readchar() # key → AL
|
|
196
|
+
osmk.writechar() # echo it
|
|
197
|
+
osmk.jmp("loop")
|
|
198
|
+
|
|
199
|
+
osmk.build()
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Counter
|
|
203
|
+
```python
|
|
204
|
+
import osmk
|
|
205
|
+
from osmk import ax, cx
|
|
206
|
+
|
|
207
|
+
osmk.start("counter.img")
|
|
208
|
+
|
|
209
|
+
osmk.ax(0)
|
|
210
|
+
osmk.cx(16)
|
|
211
|
+
|
|
212
|
+
osmk.label("count")
|
|
213
|
+
osmk.writehex()
|
|
214
|
+
osmk.prints(" ")
|
|
215
|
+
osmk.inc(ax)
|
|
216
|
+
osmk.dec(cx)
|
|
217
|
+
osmk.jnz("count")
|
|
218
|
+
|
|
219
|
+
osmk.halt()
|
|
220
|
+
osmk.build()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## How It Works
|
|
224
|
+
|
|
225
|
+
1. Each `osmk.*` call appends an x86 assembly instruction to an internal list
|
|
226
|
+
2. `build()` wraps them in a bootloader template (16-bit real mode, MBR)
|
|
227
|
+
3. NASM assembles it into a flat binary
|
|
228
|
+
4. The binary is exactly 512 bytes with the `0xAA55` boot signature
|
|
229
|
+
|
|
230
|
+
The generated code runs in **16-bit real mode** using BIOS interrupts for I/O. Use `dump_asm()` to see exactly what assembly your Python generates.
|
|
231
|
+
|
|
232
|
+
## Resources
|
|
233
|
+
|
|
234
|
+
- [OSDev Wiki](https://wiki.osdev.org/) — OS development reference
|
|
235
|
+
- [NASM Documentation](https://nasm.us/doc/) — assembler docs
|
|
236
|
+
- [x86 Instruction Reference](https://www.felixcloutier.com/x86/) — complete instruction set
|
|
237
|
+
- [Writing a Simple OS](https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) — Nick Blundell's guide
|
|
238
|
+
- [BIOS Interrupt List](https://en.wikipedia.org/wiki/BIOS_interrupt_call) — int 0x10, 0x13, 0x16, etc.
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT
|
osmk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# osmk
|
|
2
|
+
|
|
3
|
+
**Write x86 operating systems in Python — one instruction at a time.**
|
|
4
|
+
|
|
5
|
+
Every function call maps to an x86 assembly instruction. `build()` assembles it into a bootable 512-byte disk image.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install osmk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You also need **NASM** (the assembler) and optionally **QEMU** (to run your OS):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Ubuntu/Debian
|
|
17
|
+
sudo apt install nasm qemu-system-x86
|
|
18
|
+
|
|
19
|
+
# macOS
|
|
20
|
+
brew install nasm qemu
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import osmk
|
|
27
|
+
from osmk import ax, bx
|
|
28
|
+
|
|
29
|
+
osmk.start("hello.img") # begin a new OS
|
|
30
|
+
|
|
31
|
+
osmk.ax(5) # mov ax, 5
|
|
32
|
+
osmk.bx(5) # mov bx, 5
|
|
33
|
+
osmk.add(ax, bx) # add ax, bx → ax = 0x000A
|
|
34
|
+
osmk.writehex() # print AX as hex → "000A"
|
|
35
|
+
|
|
36
|
+
osmk.build() # assemble → hello.img (512 bytes)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Run it:
|
|
40
|
+
```bash
|
|
41
|
+
qemu-system-i386 -drive format=raw,file=hello.img
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API Reference
|
|
45
|
+
|
|
46
|
+
### Lifecycle
|
|
47
|
+
|
|
48
|
+
| Function | Description |
|
|
49
|
+
|----------|-------------|
|
|
50
|
+
| `start(filename, bits=16, org=0x7C00)` | Begin a new OS program |
|
|
51
|
+
| `build()` | Assemble and create the bootable image |
|
|
52
|
+
| `dump_asm()` | Return the generated NASM source (for debugging) |
|
|
53
|
+
| `run(memory=128)` | Build and launch in QEMU |
|
|
54
|
+
|
|
55
|
+
### Register Setters
|
|
56
|
+
|
|
57
|
+
Every register is callable. `osmk.ax(5)` → `mov ax, 5`.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
osmk.ax(5) # mov ax, 5
|
|
61
|
+
osmk.bx(ax) # mov bx, ax
|
|
62
|
+
osmk.al(0x41) # mov al, 0x41
|
|
63
|
+
osmk.eax(0x1000) # mov eax, 0x1000
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Available: `ax bx cx dx si di sp bp` · `al ah bl bh cl ch dl dh` · `eax ebx ecx edx esi edi esp ebp`
|
|
67
|
+
|
|
68
|
+
### Instructions
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# Arithmetic
|
|
72
|
+
osmk.add(ax, bx) osmk.sub(ax, 1)
|
|
73
|
+
osmk.mul(bx) osmk.div(cx)
|
|
74
|
+
osmk.inc(ax) osmk.dec(cx)
|
|
75
|
+
osmk.neg(ax)
|
|
76
|
+
|
|
77
|
+
# Bitwise
|
|
78
|
+
osmk.and_(ax, 0xFF) osmk.or_(ax, bx)
|
|
79
|
+
osmk.xor(ax, ax) osmk.not_(ax)
|
|
80
|
+
osmk.shl(ax, 4) osmk.shr(ax, 1)
|
|
81
|
+
|
|
82
|
+
# Compare & Jump
|
|
83
|
+
osmk.cmp(ax, 0)
|
|
84
|
+
osmk.je("done") osmk.jne("loop")
|
|
85
|
+
osmk.jl("less") osmk.jg("greater")
|
|
86
|
+
osmk.jz("zero") osmk.jnz("nonzero")
|
|
87
|
+
osmk.jmp("start") osmk.jc("carry")
|
|
88
|
+
|
|
89
|
+
# Stack
|
|
90
|
+
osmk.push(ax) osmk.pop(bx)
|
|
91
|
+
|
|
92
|
+
# Subroutines
|
|
93
|
+
osmk.call("myfunc") osmk.ret()
|
|
94
|
+
|
|
95
|
+
# Interrupts
|
|
96
|
+
osmk.interrupt(0x10) # int 0x10
|
|
97
|
+
osmk.cli() osmk.sti()
|
|
98
|
+
osmk.hlt()
|
|
99
|
+
|
|
100
|
+
# Labels
|
|
101
|
+
osmk.label("loop")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Output Helpers
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
osmk.writehex() # print AX as 4-digit hex (e.g. "000A")
|
|
108
|
+
osmk.writechar("A") # print character 'A'
|
|
109
|
+
osmk.writechar() # print whatever's in AL
|
|
110
|
+
osmk.prints("Hello!") # print a string
|
|
111
|
+
osmk.newline() # print CR+LF
|
|
112
|
+
osmk.readchar() # wait for keypress → AL
|
|
113
|
+
osmk.halt() # halt CPU (cli + hlt loop)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Custom / Escape Hatch
|
|
117
|
+
|
|
118
|
+
When osmk doesn't cover what you need:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# Inject raw NASM assembly
|
|
122
|
+
osmk.asm("mov ah, 0x0E")
|
|
123
|
+
osmk.asm("""
|
|
124
|
+
mov ah, 0x00
|
|
125
|
+
mov al, 0x03
|
|
126
|
+
int 0x10
|
|
127
|
+
""")
|
|
128
|
+
|
|
129
|
+
# Inject raw bytes
|
|
130
|
+
osmk.raw(b"\x90\x90\x90") # 3x NOP
|
|
131
|
+
|
|
132
|
+
# Define data
|
|
133
|
+
osmk.data("my_var", "dw 0x1234")
|
|
134
|
+
|
|
135
|
+
# Define a subroutine
|
|
136
|
+
with osmk.function("beep") as fn:
|
|
137
|
+
fn.asm("mov ah, 0x0E")
|
|
138
|
+
fn.asm("mov al, 0x07") # BEL character
|
|
139
|
+
fn.asm("int 0x10")
|
|
140
|
+
|
|
141
|
+
osmk.call("beep")
|
|
142
|
+
|
|
143
|
+
# Add comments
|
|
144
|
+
osmk.comment("this sets up video mode")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Explicit MOV
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
osmk.mov(ax, 0x0E) # same as osmk.ax(0x0E)
|
|
151
|
+
osmk.mov(bx, ax) # same as osmk.bx(ax)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Examples
|
|
155
|
+
|
|
156
|
+
### Hello World
|
|
157
|
+
```python
|
|
158
|
+
import osmk
|
|
159
|
+
|
|
160
|
+
osmk.start("hello.img")
|
|
161
|
+
osmk.prints("Hello, World!")
|
|
162
|
+
osmk.newline()
|
|
163
|
+
osmk.prints("My first OS!")
|
|
164
|
+
osmk.halt()
|
|
165
|
+
osmk.build()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Keyboard Echo
|
|
169
|
+
```python
|
|
170
|
+
import osmk
|
|
171
|
+
from osmk import cx
|
|
172
|
+
|
|
173
|
+
osmk.start("echo.img")
|
|
174
|
+
osmk.prints("Type anything:")
|
|
175
|
+
osmk.newline()
|
|
176
|
+
|
|
177
|
+
osmk.label("loop")
|
|
178
|
+
osmk.readchar() # key → AL
|
|
179
|
+
osmk.writechar() # echo it
|
|
180
|
+
osmk.jmp("loop")
|
|
181
|
+
|
|
182
|
+
osmk.build()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Counter
|
|
186
|
+
```python
|
|
187
|
+
import osmk
|
|
188
|
+
from osmk import ax, cx
|
|
189
|
+
|
|
190
|
+
osmk.start("counter.img")
|
|
191
|
+
|
|
192
|
+
osmk.ax(0)
|
|
193
|
+
osmk.cx(16)
|
|
194
|
+
|
|
195
|
+
osmk.label("count")
|
|
196
|
+
osmk.writehex()
|
|
197
|
+
osmk.prints(" ")
|
|
198
|
+
osmk.inc(ax)
|
|
199
|
+
osmk.dec(cx)
|
|
200
|
+
osmk.jnz("count")
|
|
201
|
+
|
|
202
|
+
osmk.halt()
|
|
203
|
+
osmk.build()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## How It Works
|
|
207
|
+
|
|
208
|
+
1. Each `osmk.*` call appends an x86 assembly instruction to an internal list
|
|
209
|
+
2. `build()` wraps them in a bootloader template (16-bit real mode, MBR)
|
|
210
|
+
3. NASM assembles it into a flat binary
|
|
211
|
+
4. The binary is exactly 512 bytes with the `0xAA55` boot signature
|
|
212
|
+
|
|
213
|
+
The generated code runs in **16-bit real mode** using BIOS interrupts for I/O. Use `dump_asm()` to see exactly what assembly your Python generates.
|
|
214
|
+
|
|
215
|
+
## Resources
|
|
216
|
+
|
|
217
|
+
- [OSDev Wiki](https://wiki.osdev.org/) — OS development reference
|
|
218
|
+
- [NASM Documentation](https://nasm.us/doc/) — assembler docs
|
|
219
|
+
- [x86 Instruction Reference](https://www.felixcloutier.com/x86/) — complete instruction set
|
|
220
|
+
- [Writing a Simple OS](https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) — Nick Blundell's guide
|
|
221
|
+
- [BIOS Interrupt List](https://en.wikipedia.org/wiki/BIOS_interrupt_call) — int 0x10, 0x13, 0x16, etc.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|