dt31 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.
- dt31-0.1.0/PKG-INFO +546 -0
- dt31-0.1.0/README.md +524 -0
- dt31-0.1.0/pyproject.toml +48 -0
- dt31-0.1.0/src/dt31/__init__.py +9 -0
- dt31-0.1.0/src/dt31/assembler.py +208 -0
- dt31-0.1.0/src/dt31/cli.py +551 -0
- dt31-0.1.0/src/dt31/cpu.py +439 -0
- dt31-0.1.0/src/dt31/exceptions.py +23 -0
- dt31-0.1.0/src/dt31/instructions.py +1323 -0
- dt31-0.1.0/src/dt31/operands.py +480 -0
- dt31-0.1.0/src/dt31/parser.py +182 -0
dt31-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: dt31
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A toy computer in Python.
|
|
5
|
+
Keywords: emulator,assembly,computer,virtual-machine,instruction-set,toy-computer,cpu,interpreter
|
|
6
|
+
Author: Dan Turkel
|
|
7
|
+
Author-email: Dan Turkel <daturkel@gmail.com>
|
|
8
|
+
License: MIT License
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
18
|
+
Maintainer: Dan Turkel
|
|
19
|
+
Maintainer-email: Dan Turkel <daaturkel@gmail.com>
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# dt31
|
|
24
|
+
|
|
25
|
+
[](https://opensource.org/licenses/MIT) [](https://pdoc.dev/docs/pdoc.html) [](https://github.com/astral-sh/ruff) 
|
|
26
|
+
|
|
27
|
+
A toy computer emulator and assembly language written in Python. Build programs with 60+ built-in instructions for interacting with registers, memory, and the stack. Write your programs in the native assembly syntax or directly with the Python API.
|
|
28
|
+
|
|
29
|
+
<table>
|
|
30
|
+
<tr>
|
|
31
|
+
<th>countdown.dt</th>
|
|
32
|
+
<th>countdown.py</th>
|
|
33
|
+
</tr>
|
|
34
|
+
<tr>
|
|
35
|
+
<td>
|
|
36
|
+
|
|
37
|
+
```nasm
|
|
38
|
+
; countdown.dt
|
|
39
|
+
|
|
40
|
+
; copy 5 to register a
|
|
41
|
+
CP 5, R.a
|
|
42
|
+
loop:
|
|
43
|
+
; print register a
|
|
44
|
+
NOUT R.a, 1
|
|
45
|
+
; a = a - 1
|
|
46
|
+
SUB R.a, 1
|
|
47
|
+
; jump to loop if a > 0
|
|
48
|
+
JGT loop, R.a, 0
|
|
49
|
+
|
|
50
|
+
; run with: `dt31 countdown.dt`
|
|
51
|
+
; output: 5 4 3 2 1
|
|
52
|
+
```
|
|
53
|
+
</td>
|
|
54
|
+
<td>
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from dt31 import DT31, I, L, Label, R
|
|
58
|
+
|
|
59
|
+
# create vm with default settings
|
|
60
|
+
cpu = DT31()
|
|
61
|
+
|
|
62
|
+
program = [
|
|
63
|
+
I.CP(5, R.a),
|
|
64
|
+
loop := Label("loop"),
|
|
65
|
+
I.NOUT(R.a, L[1]),
|
|
66
|
+
I.SUB(R.a, L[1]),
|
|
67
|
+
I.JGT(loop, R.a, L[0]),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
cpu.run(program)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
</td>
|
|
74
|
+
|
|
75
|
+
</tr>
|
|
76
|
+
</table>
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- **Simple CPU Architecture**: Configurable registers, fixed-size memory, and stack-based operations
|
|
81
|
+
- **Rich Instruction Set**: 60+ instructions including arithmetic, bitwise operations, logic, control flow, and I/O
|
|
82
|
+
- **Assembly Support**: Two-pass assembler with label resolution for jumps and function calls
|
|
83
|
+
- **Assembly Parser**: Parse and execute `.dt` assembly files with text-based syntax
|
|
84
|
+
- **Command-Line Interface**: Execute `.dt` files directly with the `dt31` command
|
|
85
|
+
- **Python API**: Build and run programs programmatically with an intuitive API
|
|
86
|
+
- **Debug Mode**: Step-by-step execution with state inspection and breakpoints
|
|
87
|
+
- **Pure Python**: Zero dependencies
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
pip install dt31
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Getting Started
|
|
96
|
+
|
|
97
|
+
### Hello World
|
|
98
|
+
|
|
99
|
+
Create a file `hello.dt`
|
|
100
|
+
|
|
101
|
+
```nasm
|
|
102
|
+
; Output "Hi!"
|
|
103
|
+
OOUT 'H', 0
|
|
104
|
+
OOUT 'i', 0
|
|
105
|
+
OOUT '!', 0
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
and run it with the `dt31` interpreter
|
|
109
|
+
|
|
110
|
+
```shell
|
|
111
|
+
dt31 hello.dt
|
|
112
|
+
# Hi!
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Basic Arithmetic
|
|
116
|
+
|
|
117
|
+
```nasm
|
|
118
|
+
; Add two numbers
|
|
119
|
+
CP 10, R.a ; Copy 10 into register a
|
|
120
|
+
CP 5, R.b ; Copy 5 into register b
|
|
121
|
+
ADD R.a, R.b ; a = a + b
|
|
122
|
+
NOUT R.a, 1 ; Output a with newline
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Save as `add.dt` and run: `dt31 add.dt` to output `15`.
|
|
126
|
+
|
|
127
|
+
### Loops with Labels
|
|
128
|
+
|
|
129
|
+
```nasm
|
|
130
|
+
; Count from 1 to 10
|
|
131
|
+
CP 1, R.a ; Start counter at 1
|
|
132
|
+
loop:
|
|
133
|
+
NOUT R.a, 1 ; Print counter
|
|
134
|
+
ADD R.a, 1 ; Increment counter
|
|
135
|
+
JLT loop, R.a, 11 ; Jump to loop if a < 11
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Save as `count.dt` and run: `dt31 count.dt` to output `1 2 3 4 5 6 7 8 9 10`.
|
|
139
|
+
|
|
140
|
+
### Function Calls
|
|
141
|
+
|
|
142
|
+
```nasm
|
|
143
|
+
; Print a greeting multiple times
|
|
144
|
+
CP 3, R.a ; Counter: print 3 times
|
|
145
|
+
print_loop:
|
|
146
|
+
CALL greet ; Call the greeting function
|
|
147
|
+
SUB R.a, 1. ; R.a -= 1
|
|
148
|
+
JGT print_loop, R.a, 0 ; loop if R.a > 0
|
|
149
|
+
JMP end
|
|
150
|
+
|
|
151
|
+
greet:
|
|
152
|
+
; Reusable greeting function
|
|
153
|
+
OOUT 'H', 0
|
|
154
|
+
OOUT 'i', 0
|
|
155
|
+
OOUT '!', 0
|
|
156
|
+
OOUT ' ', 0
|
|
157
|
+
RET
|
|
158
|
+
|
|
159
|
+
end:
|
|
160
|
+
; output: `Hi! Hi! Hi! `
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Functions use the stack for return addresses and can be called multiple times. See the [examples](https://github.com/daturkel/dt31/tree/main/examples) directory for more complex examples.
|
|
164
|
+
|
|
165
|
+
## Core Concepts
|
|
166
|
+
|
|
167
|
+
### Operands
|
|
168
|
+
|
|
169
|
+
dt31 provides several operand types for referencing values:
|
|
170
|
+
|
|
171
|
+
- **Literals**: Constant values `L[42]`, or `LC["a"]` as a shortcut for `L[ord("a")]`
|
|
172
|
+
- **Registers**: CPU registers `R.a`, `R.b`, `R.c`
|
|
173
|
+
- **Memory**: Memory addresses `M[100]`, indirect addressing `M[R.a]`
|
|
174
|
+
- **Labels**: Named jump targets `Label("loop")`
|
|
175
|
+
|
|
176
|
+
See the [operands documentation](https://daturkel.github.io/dt31/dt31/operands.html) for details.
|
|
177
|
+
|
|
178
|
+
### Instructions
|
|
179
|
+
|
|
180
|
+
The instruction set includes:
|
|
181
|
+
|
|
182
|
+
- **Arithmetic**: `ADD`, `SUB`, `MUL`, `DIV`, `MOD`
|
|
183
|
+
- **Bitwise**: `BAND`, `BOR`, `BXOR`, `BNOT`, `BSL`, `BSR`
|
|
184
|
+
- **Comparisons**: `LT`, `GT`, `LE`, `GE`, `EQ`, `NE`
|
|
185
|
+
- **Logic**: `AND`, `OR`, `XOR`, `NOT`
|
|
186
|
+
- **Control Flow**: `JMP`, `RJMP`, `JEQ`, `JNE`, `JGT`, `JGE`, `JIF`
|
|
187
|
+
- **Functions**: `CALL`, `RCALL`, `RET`
|
|
188
|
+
- **Stack**: `PUSH`, `POP`, `SEMP`
|
|
189
|
+
- **I/O**: `NOUT`, `OOUT`, `NIN`, `OIN`
|
|
190
|
+
- **Data Movement**: `CP`
|
|
191
|
+
|
|
192
|
+
Users can easily define their own custom instructions by subclassing `dt31.instructions.Instruction`.
|
|
193
|
+
|
|
194
|
+
See the [instructions documentation](https://daturkel.github.io/dt31/dt31/instructions.html) for the complete reference.
|
|
195
|
+
|
|
196
|
+
### CPU Architecture
|
|
197
|
+
|
|
198
|
+
The DT31 CPU includes:
|
|
199
|
+
|
|
200
|
+
- **Registers**: General-purpose registers (default: `a`, `b`, and `c`)
|
|
201
|
+
- **Memory**: Fixed-size byte array (default: 256 slots)
|
|
202
|
+
- **Stack**: For temporary values and function calls (default: 256 slots)
|
|
203
|
+
- **Instruction Pointer**: Tracks current instruction in register `ip`
|
|
204
|
+
|
|
205
|
+
See the [CPU documentation](https://daturkel.github.io/dt31/dt31/cpu.html) for API details.
|
|
206
|
+
|
|
207
|
+
## Command-Line Interface
|
|
208
|
+
|
|
209
|
+
### Basic Usage
|
|
210
|
+
|
|
211
|
+
Execute `.dt` assembly files directly:
|
|
212
|
+
|
|
213
|
+
```shell
|
|
214
|
+
dt31 program.dt
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### CLI Options
|
|
218
|
+
|
|
219
|
+
- `--debug` or `-d`: Enable step-by-step debug output
|
|
220
|
+
- `--parse-only` or `-p`: Validate syntax without executing
|
|
221
|
+
- `--registers a,b,c,d`: Specify custom registers (auto-detected by default)
|
|
222
|
+
- `--memory 512`: Set memory size in bytes (default: 256)
|
|
223
|
+
- `--stack-size 512`: Set stack size (default: 256)
|
|
224
|
+
- `--custom-instructions PATH` or `-i PATH`: Load custom instruction definitions from a Python file
|
|
225
|
+
- `--dump {none,error,success,all}`: When to dump CPU state (default: none)
|
|
226
|
+
- `--dump-file FILE`: File path for CPU state dump (auto-generates timestamped filename if not specified)
|
|
227
|
+
|
|
228
|
+
**Examples:**
|
|
229
|
+
|
|
230
|
+
```shell
|
|
231
|
+
# Parse and validate only (no execution)
|
|
232
|
+
dt31 --parse-only program.dt
|
|
233
|
+
|
|
234
|
+
# Run with debug output
|
|
235
|
+
dt31 --debug program.dt
|
|
236
|
+
|
|
237
|
+
# Use custom memory size
|
|
238
|
+
dt31 --memory 1024 program.dt
|
|
239
|
+
|
|
240
|
+
# Specify registers explicitly
|
|
241
|
+
dt31 --registers a,b,c,d,e program.dt
|
|
242
|
+
|
|
243
|
+
# Use custom instructions
|
|
244
|
+
dt31 --custom-instructions my_instructions.py program.dt
|
|
245
|
+
|
|
246
|
+
# Dump CPU state on error (for debugging crashes)
|
|
247
|
+
dt31 --dump error program.dt # Auto-generates program_crash_TIMESTAMP.json
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
See the [CLI documentation](https://daturkel.github.io/dt31/dt31/cli.html) for complete details.
|
|
251
|
+
|
|
252
|
+
### Custom Instructions
|
|
253
|
+
|
|
254
|
+
Define custom instructions in a Python file and load them with `--custom-instructions`. Your file must export an `INSTRUCTIONS` dict and instruction names must be all-caps:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
# my_instructions.py
|
|
258
|
+
from dt31.instructions import UnaryOperation
|
|
259
|
+
from dt31.operands import Operand, Reference
|
|
260
|
+
|
|
261
|
+
class TRIPLE(UnaryOperation):
|
|
262
|
+
"""Triple a value."""
|
|
263
|
+
def __init__(self, a: Operand, out: Reference | None = None):
|
|
264
|
+
super().__init__("TRIPLE", a, out)
|
|
265
|
+
|
|
266
|
+
def _calc(self, cpu: "DT31") -> int:
|
|
267
|
+
return self.a.resolve(cpu) * 3
|
|
268
|
+
|
|
269
|
+
INSTRUCTIONS = {"TRIPLE": TRIPLE}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Use in assembly:
|
|
273
|
+
|
|
274
|
+
```nasm
|
|
275
|
+
CP 5, R.a
|
|
276
|
+
TRIPLE R.a
|
|
277
|
+
NOUT R.a, 1 ; Outputs 15
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Run with: `dt31 --custom-instructions my_instructions.py program.dt`
|
|
281
|
+
|
|
282
|
+
**Security Warning**: Loading custom instruction files executes arbitrary Python code. Only load files from trusted sources.
|
|
283
|
+
|
|
284
|
+
See the [instructions documentation](https://daturkel.github.io/dt31/dt31/instructions.html) for more details on creating custom instructions.
|
|
285
|
+
|
|
286
|
+
### CPU State Dumps
|
|
287
|
+
|
|
288
|
+
The `--dump` option captures complete CPU state to JSON for debugging. Dumps include:
|
|
289
|
+
|
|
290
|
+
- **CPU state**: registers, memory, stack, and loaded program (as assembly text)
|
|
291
|
+
- **Error information** (on error dumps): exception type, message, traceback, and the instruction that caused the error
|
|
292
|
+
|
|
293
|
+
Error dumps include both `repr` and `str` formats of the failing instruction for easier debugging:
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"cpu_state": {
|
|
298
|
+
"registers": {"a": 10, "b": 0, "ip": 2},
|
|
299
|
+
"memory": [...],
|
|
300
|
+
"stack": [],
|
|
301
|
+
"program": "CP 10, R.a\nCP 0, R.b\nDIV R.a, R.b",
|
|
302
|
+
"config": {"memory_size": 256, "stack_size": 256, "wrap_memory": false}
|
|
303
|
+
},
|
|
304
|
+
"error": {
|
|
305
|
+
"type": "ZeroDivisionError",
|
|
306
|
+
"message": "integer division or modulo by zero",
|
|
307
|
+
"instruction": {
|
|
308
|
+
"repr": "DIV(a=R.a, b=R.b, out=R.a)",
|
|
309
|
+
"str": "DIV R.a, R.b, R.a"
|
|
310
|
+
},
|
|
311
|
+
"traceback": "..."
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Assembly Language Reference
|
|
317
|
+
|
|
318
|
+
### Syntax Rules
|
|
319
|
+
|
|
320
|
+
**Instructions and Operands:**
|
|
321
|
+
```nasm
|
|
322
|
+
INSTRUCTION operand1, operand2, operand3
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
- Instructions are case-insensitive (`ADD`, `add`, and `Add` are all valid)
|
|
326
|
+
- Register names and label names are case-*sensitive*
|
|
327
|
+
- Operands are separated by commas (spaces around commas are optional)
|
|
328
|
+
- Comments start with `;` and continue to end of line
|
|
329
|
+
- Blank lines and indentation are ignored
|
|
330
|
+
|
|
331
|
+
### Operand Types
|
|
332
|
+
|
|
333
|
+
The assembly text syntax differs from Python syntax:
|
|
334
|
+
|
|
335
|
+
| Operand Type | Assembly Syntax | Python Syntax | Example |
|
|
336
|
+
|--------------|---------------|-------------|---------|
|
|
337
|
+
| **Numeric Literal** | `42`, `-5` | `L[42]`, `L[-5]` | `CP 42, R.a` |
|
|
338
|
+
| **Character Literal** | `'A'` | `LC["A"]` | `OOUT 'H', 0` |
|
|
339
|
+
| **Register** | `R.a` | `R.a` | `ADD R.a, R.b` |
|
|
340
|
+
| **Memory (direct)** | `[100]` or `M[100]` | `M[100]` | `CP 42, [100]` |
|
|
341
|
+
| **Memory (indirect)** | `[R.a]` or `M[R.a]` | `M[R.a]` | `CP [R.a], R.b` |
|
|
342
|
+
| **Label** | `loop` | `Label("loop")` | `JMP loop` |
|
|
343
|
+
|
|
344
|
+
**Key Differences:**
|
|
345
|
+
|
|
346
|
+
1. **Literals**: In text syntax, bare numbers are literals (no `L[...]` wrapper needed)
|
|
347
|
+
2. **Characters**: Use single quotes `'A'` instead of `LC["A"]`
|
|
348
|
+
3. **Memory**: The `M` prefix is optional (both `[100]` and `M[100]` work)
|
|
349
|
+
4. **Labels**: Bare identifiers are labels (no `Label(...)` constructor needed)
|
|
350
|
+
5. **Registers**: **Must** use `R.` prefix in both syntaxes
|
|
351
|
+
|
|
352
|
+
### Label Definition
|
|
353
|
+
|
|
354
|
+
Labels mark positions in code:
|
|
355
|
+
|
|
356
|
+
```nasm
|
|
357
|
+
; Label on its own line
|
|
358
|
+
loop:
|
|
359
|
+
ADD R.a, 1
|
|
360
|
+
JLT loop, R.a, 10
|
|
361
|
+
|
|
362
|
+
; Label on same line as instruction
|
|
363
|
+
start: CP 0, R.a
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Label names must contain only alphanumeric characters and underscores.
|
|
367
|
+
|
|
368
|
+
## Python API
|
|
369
|
+
|
|
370
|
+
While assembly syntax is the primary way to use dt31, you can also build and run programs programmatically using the Python API.
|
|
371
|
+
|
|
372
|
+
### Creating and Running Programs
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
from dt31 import DT31, I, L, M, R
|
|
376
|
+
|
|
377
|
+
# Create CPU instance
|
|
378
|
+
cpu = DT31()
|
|
379
|
+
|
|
380
|
+
# Write program as list of instructions
|
|
381
|
+
program = [
|
|
382
|
+
I.CP(42, R.a),
|
|
383
|
+
I.CP(100, M[R.a]),
|
|
384
|
+
I.NOUT(R.a, L[1]),
|
|
385
|
+
I.NOUT(M[R.a], L[1])
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
# Run the program
|
|
389
|
+
cpu.run(program)
|
|
390
|
+
# 42
|
|
391
|
+
# 100
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Parsing Assembly from Python
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
from dt31 import DT31
|
|
398
|
+
from dt31.parser import parse_program
|
|
399
|
+
|
|
400
|
+
cpu = DT31()
|
|
401
|
+
|
|
402
|
+
assembly = """
|
|
403
|
+
CP 5, R.a
|
|
404
|
+
loop:
|
|
405
|
+
NOUT R.a, 1
|
|
406
|
+
SUB R.a, 1
|
|
407
|
+
JGT loop, R.a, 0
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
program = parse_program(assembly)
|
|
411
|
+
cpu.run(program)
|
|
412
|
+
# 5 4 3 2 1
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Converting Programs to Text
|
|
416
|
+
|
|
417
|
+
Convert Python programs to assembly text format:
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
from dt31 import I, L, Label, LC, R
|
|
421
|
+
from dt31.assembler import program_to_text
|
|
422
|
+
|
|
423
|
+
# Create a program in Python
|
|
424
|
+
program = [
|
|
425
|
+
I.CP(5, R.a),
|
|
426
|
+
loop := Label("loop"),
|
|
427
|
+
I.OOUT(LC["*"]),
|
|
428
|
+
I.SUB(R.a, L[1]),
|
|
429
|
+
I.JGT(loop, R.a, L[0]),
|
|
430
|
+
]
|
|
431
|
+
|
|
432
|
+
# Convert to assembly text
|
|
433
|
+
text = program_to_text(program)
|
|
434
|
+
print(text)
|
|
435
|
+
# CP 5, R.a
|
|
436
|
+
# loop:
|
|
437
|
+
# OOUT '*', 0
|
|
438
|
+
# SUB R.a, 1, R.a
|
|
439
|
+
# JGT loop, R.a, 0
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Labels and Function Calls
|
|
443
|
+
|
|
444
|
+
Labels offer a great use-case for the Python walrus operator.
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
from dt31.operands import I, LC, Label
|
|
448
|
+
|
|
449
|
+
program = [
|
|
450
|
+
I.CALL(print_hi := Label("print_hi")),
|
|
451
|
+
I.JMP(end := Label("end")),
|
|
452
|
+
|
|
453
|
+
print_hi,
|
|
454
|
+
I.OOUT(LC['H']),
|
|
455
|
+
I.OOUT(LC['i']),
|
|
456
|
+
I.RET(),
|
|
457
|
+
|
|
458
|
+
end,
|
|
459
|
+
]
|
|
460
|
+
cpu.run(program)
|
|
461
|
+
# Hi
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Debugging with Step Execution
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
cpu = DT31()
|
|
468
|
+
cpu.load(program)
|
|
469
|
+
|
|
470
|
+
# Execute one instruction at a time
|
|
471
|
+
cpu.step(debug=True) # Prints instruction and state
|
|
472
|
+
print(cpu.state) # Inspect CPU state
|
|
473
|
+
|
|
474
|
+
# Execute a full program one instruction at a time
|
|
475
|
+
cpu.run(program, debug=True)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Accessing CPU State
|
|
479
|
+
|
|
480
|
+
```python
|
|
481
|
+
# Get register values
|
|
482
|
+
value = cpu.get_register('a')
|
|
483
|
+
|
|
484
|
+
# Set register values
|
|
485
|
+
cpu.set_register('b', 42)
|
|
486
|
+
|
|
487
|
+
# Access memory
|
|
488
|
+
cpu.set_memory(100, 255)
|
|
489
|
+
byte = cpu.get_memory(100)
|
|
490
|
+
|
|
491
|
+
# Get full state snapshot
|
|
492
|
+
state = cpu.state # Returns dict with registers, memory, stack, ip
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Documentation
|
|
496
|
+
|
|
497
|
+
Full API documentation is available at [the docs site](https://daturkel.github.io/dt31/dt31.html). Generate the latest docs with:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
uv run invoke docs
|
|
501
|
+
uv run invoke serve-docs # Serve locally at http://localhost:8080
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Key documentation pages:
|
|
505
|
+
|
|
506
|
+
- [DT31 CPU Class](https://daturkel.github.io/dt31/dt31/cpu.html) - CPU methods and state management
|
|
507
|
+
- [Instructions](https://daturkel.github.io/dt31/dt31/instructions.html) - Complete instruction reference
|
|
508
|
+
- [Operands](https://daturkel.github.io/dt31/dt31/operands.html) - Operand types and usage
|
|
509
|
+
- [Parser](https://daturkel.github.io/dt31/dt31/parser.html) - Assembly text parsing
|
|
510
|
+
- [Assembler](https://daturkel.github.io/dt31/dt31/assembler.html) - Label resolution and assembly
|
|
511
|
+
- [CLI](https://daturkel.github.io/dt31/dt31/cli.html) - Command-line interface
|
|
512
|
+
|
|
513
|
+
## Development
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# Install dependencies
|
|
517
|
+
uv sync --dev
|
|
518
|
+
|
|
519
|
+
# Set up pre-commit hooks
|
|
520
|
+
pre-commit install
|
|
521
|
+
pre-commit install --hook-type pre-push
|
|
522
|
+
|
|
523
|
+
# Run tests
|
|
524
|
+
uv run invoke test
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
DT31 is open-source and contributors are welcome on [Github](https://github.com/daturkel/dt31).
|
|
528
|
+
|
|
529
|
+
## Roadmap
|
|
530
|
+
|
|
531
|
+
- [x] Character literals?
|
|
532
|
+
- [x] Parse and execute `.dt` files
|
|
533
|
+
- [x] Breakpoint instruction
|
|
534
|
+
- [x] Clearer handing of input during debug mode
|
|
535
|
+
- [x] Python to text output
|
|
536
|
+
- [ ] User-definable macros (in both python and assembly syntax)
|
|
537
|
+
- [ ] File I/O
|
|
538
|
+
- [ ] Data handling
|
|
539
|
+
- [ ] Input error-handling
|
|
540
|
+
- [ ] Preserve comments in parser
|
|
541
|
+
- [ ] Formatter
|
|
542
|
+
- [ ] Interpreter resume from dump
|
|
543
|
+
|
|
544
|
+
## License
|
|
545
|
+
|
|
546
|
+
MIT License
|