taracpu 1.0.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.
- taracpu-1.0.0/PKG-INFO +105 -0
- taracpu-1.0.0/README.md +95 -0
- taracpu-1.0.0/pyproject.toml +29 -0
- taracpu-1.0.0/setup.cfg +4 -0
- taracpu-1.0.0/src/__init__.py +1 -0
- taracpu-1.0.0/src/assembler/__init__.py +1 -0
- taracpu-1.0.0/src/assembler/asm.py +241 -0
- taracpu-1.0.0/src/assets/delete.jpeg +0 -0
- taracpu-1.0.0/src/assets/logo/logo1.png +0 -0
- taracpu-1.0.0/src/assets/logo/logo2.png +0 -0
- taracpu-1.0.0/src/assets/logo/logo2_cropped.png +0 -0
- taracpu-1.0.0/src/assets/logo/logo2_text.png +0 -0
- taracpu-1.0.0/src/assets/rename.png +0 -0
- taracpu-1.0.0/src/examples/__init__.py +1 -0
- taracpu-1.0.0/src/examples/examples.py +17 -0
- taracpu-1.0.0/src/examples/progs/binary_search.tara +71 -0
- taracpu-1.0.0/src/examples/progs/display_bouncing_ball.tara +349 -0
- taracpu-1.0.0/src/examples/progs/display_rectangle.tara +70 -0
- taracpu-1.0.0/src/examples/progs/display_triangle.tara +80 -0
- taracpu-1.0.0/src/examples/progs/drawline.tara +380 -0
- taracpu-1.0.0/src/examples/progs/factorial_via_mul_n_6.tara +9 -0
- taracpu-1.0.0/src/examples/progs/fibonacci_n_10.tara +14 -0
- taracpu-1.0.0/src/examples/progs/gcd_a_48_b_18.tara +12 -0
- taracpu-1.0.0/src/examples/progs/generate_prime.tara +62 -0
- taracpu-1.0.0/src/examples/progs/image.tara +18 -0
- taracpu-1.0.0/src/examples/progs/matrix_multiplication.tara +224 -0
- taracpu-1.0.0/src/examples/progs/ping_pong.tara +206 -0
- taracpu-1.0.0/src/examples/progs/prime_test_n_17.tara +19 -0
- taracpu-1.0.0/src/examples/progs/sort_bubble.tara +49 -0
- taracpu-1.0.0/src/examples/progs/sort_insertion.tara +52 -0
- taracpu-1.0.0/src/examples/progs/testprog.tara +1 -0
- taracpu-1.0.0/src/paths.py +30 -0
- taracpu-1.0.0/src/run_sim.py +14 -0
- taracpu-1.0.0/src/simulation/__init__.py +1 -0
- taracpu-1.0.0/src/simulation/cpu.py +246 -0
- taracpu-1.0.0/src/ui/__init__.py +1 -0
- taracpu-1.0.0/src/ui/app.py +673 -0
- taracpu-1.0.0/src/ui/assets.py +49 -0
- taracpu-1.0.0/src/ui/isa_dialog.py +121 -0
- taracpu-1.0.0/src/ui/panels.py +2988 -0
- taracpu-1.0.0/src/ui/theme.py +322 -0
- taracpu-1.0.0/taracpu/__init__.py +3 -0
- taracpu-1.0.0/taracpu/__main__.py +3 -0
- taracpu-1.0.0/taracpu.egg-info/PKG-INFO +105 -0
- taracpu-1.0.0/taracpu.egg-info/SOURCES.txt +47 -0
- taracpu-1.0.0/taracpu.egg-info/dependency_links.txt +1 -0
- taracpu-1.0.0/taracpu.egg-info/entry_points.txt +2 -0
- taracpu-1.0.0/taracpu.egg-info/requires.txt +2 -0
- taracpu-1.0.0/taracpu.egg-info/top_level.txt +2 -0
taracpu-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taracpu
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: TARA CPU Simulator — 16-bit RISC teaching architecture (CS2300 at IIT Madras)
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: PyQt5>=5.15
|
|
9
|
+
Requires-Dist: matplotlib>=3.5
|
|
10
|
+
|
|
11
|
+
# TARA CPU Simulator (CS2300 at IIT Madras)
|
|
12
|
+
|
|
13
|
+
A full-featured desktop simulator for the **TARA (Teaching Architecture for RISC Assembly)** ISA from CS2300 taught at IIT Madras, refer https://cse.iitm.ac.in/~ayon/courses/CS2300/CS2300.html
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
- **Syntax-highlighted assembly editor** with line numbers and 6 built-in example programs
|
|
17
|
+
- **One-click assembler** with error reporting
|
|
18
|
+
- **Machine code listing** with hex + colored binary fields, synchronized to PC
|
|
19
|
+
- **Register file view** — 8 registers in a 4x2 visual bank, with change highlighting
|
|
20
|
+
- **Memory viewer** — browse all 2 KB of byte-addressable memory, with optional Matplotlib colormap heatmap
|
|
21
|
+
- **Display panel** — 64x64 memory-mapped raster with 1-bit packed pixels and directional input byte
|
|
22
|
+
- **Instruction decode panel** — shows format, fields, and human-readable effect
|
|
23
|
+
- **Execution log** — per-instruction trace with register changes
|
|
24
|
+
- **Step / Run / Stop / Reset** controls
|
|
25
|
+
- **Save Machine State / Boot State** controls for pausing and continuing later
|
|
26
|
+
- **Variable clock** — 1 Hz to max speed via presets or custom frequency, adjustable while running
|
|
27
|
+
|
|
28
|
+
## How to Run
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install PyQt5 matplotlib
|
|
32
|
+
python3 run_sim.py
|
|
33
|
+
# or as a package:
|
|
34
|
+
python3 -m src.run_sim
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Classroom Font Settings
|
|
38
|
+
|
|
39
|
+
The editor, machine-code listing, memory heatmap matrix, and register file use larger classroom-friendly defaults. You can also edit `config/ui_settings.json` before starting the simulator:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"base_font_size": 14,
|
|
44
|
+
"editor_font_size": 15,
|
|
45
|
+
"listing_font_size": 12,
|
|
46
|
+
"memory_font_size": 12,
|
|
47
|
+
"register_font_size": 16
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The editor, listing, and memory matrix also have `A-` / `A+` controls in the UI; those changes are saved back to the config file.
|
|
52
|
+
|
|
53
|
+
## Project Layout
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
src/
|
|
57
|
+
assembler/ TARA assembler
|
|
58
|
+
simulation/ CPU core and ISA behavior
|
|
59
|
+
ui/ PyQt application, panels, theme, dialogs
|
|
60
|
+
examples/ bundled example programs
|
|
61
|
+
assets/logo/ application logos
|
|
62
|
+
config/
|
|
63
|
+
memory/ memory CSV dumps
|
|
64
|
+
cpu_state/ machine-state JSON dumps
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Workflow
|
|
68
|
+
|
|
69
|
+
1. Write or load assembly code in the **editor** (left panel)
|
|
70
|
+
2. Click **⚙ Assemble & Load** — errors appear below the editor
|
|
71
|
+
3. Use **⏭ Step** to execute one instruction at a time
|
|
72
|
+
4. Or click **▶ Run** with a chosen clock frequency
|
|
73
|
+
5. Watch registers, memory, listing highlight, and the decode panel update in real time
|
|
74
|
+
6. Click **💾 Save Machine State** to write registers, PC, memory, counters, and source to JSON
|
|
75
|
+
7. Later click **⤴ Boot State** to restore the JSON state and continue computation
|
|
76
|
+
8. Click **↺ Reset** to reload the program and start over
|
|
77
|
+
|
|
78
|
+
## TARA ISA Quick Reference
|
|
79
|
+
|
|
80
|
+
| Format | Instructions | Encoding |
|
|
81
|
+
|--------|-------------|---------|
|
|
82
|
+
| F0 | NOP, HLT, RET | `op[15:11] 00000000000` |
|
|
83
|
+
| F1 | ADD, SUB, MUL, AND, OR, XOR, SLT | `op rd rsA rsB 00` |
|
|
84
|
+
| F2 | MOV, NOT | `op rd rs 00000` |
|
|
85
|
+
| F3 | LIL, LIH, ADDI, SHL, SHR | `op rd imm8` |
|
|
86
|
+
| F4 | LDW, STW, LDB, STB | `op rdata rbase off5` |
|
|
87
|
+
| F5 | BZ, BN | `op rtest rel8` |
|
|
88
|
+
| F6 | JMP, CALL | `op rel11` |
|
|
89
|
+
| F7 | PUSH, POP | `op rstk 00000000` |
|
|
90
|
+
|
|
91
|
+
## Memory Map
|
|
92
|
+
| Range | Use |
|
|
93
|
+
|-------|-----|
|
|
94
|
+
| 0x000–0x3FF | Program code |
|
|
95
|
+
| 0x400–0x4FF | Data / arrays |
|
|
96
|
+
| 0x500–0x5FE | Stack (R7) |
|
|
97
|
+
| 0x5FF | Display keyboard byte: bit0=left, bit1=right, bit2=up, bit3=down |
|
|
98
|
+
| 0x600–0x7FF | Display framebuffer, 64x64 pixels, 1 bit per pixel, 8 pixels per byte. Pixel values: 0 off, 1 on. |
|
|
99
|
+
|
|
100
|
+
## Register Conventions
|
|
101
|
+
| Reg | Convention |
|
|
102
|
+
|-----|-----------|
|
|
103
|
+
| R0–R5 | General purpose |
|
|
104
|
+
| R6 | Link register (CALL/RET) |
|
|
105
|
+
| R7 | Stack pointer (PUSH/POP) |
|
taracpu-1.0.0/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# TARA CPU Simulator (CS2300 at IIT Madras)
|
|
2
|
+
|
|
3
|
+
A full-featured desktop simulator for the **TARA (Teaching Architecture for RISC Assembly)** ISA from CS2300 taught at IIT Madras, refer https://cse.iitm.ac.in/~ayon/courses/CS2300/CS2300.html
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **Syntax-highlighted assembly editor** with line numbers and 6 built-in example programs
|
|
7
|
+
- **One-click assembler** with error reporting
|
|
8
|
+
- **Machine code listing** with hex + colored binary fields, synchronized to PC
|
|
9
|
+
- **Register file view** — 8 registers in a 4x2 visual bank, with change highlighting
|
|
10
|
+
- **Memory viewer** — browse all 2 KB of byte-addressable memory, with optional Matplotlib colormap heatmap
|
|
11
|
+
- **Display panel** — 64x64 memory-mapped raster with 1-bit packed pixels and directional input byte
|
|
12
|
+
- **Instruction decode panel** — shows format, fields, and human-readable effect
|
|
13
|
+
- **Execution log** — per-instruction trace with register changes
|
|
14
|
+
- **Step / Run / Stop / Reset** controls
|
|
15
|
+
- **Save Machine State / Boot State** controls for pausing and continuing later
|
|
16
|
+
- **Variable clock** — 1 Hz to max speed via presets or custom frequency, adjustable while running
|
|
17
|
+
|
|
18
|
+
## How to Run
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install PyQt5 matplotlib
|
|
22
|
+
python3 run_sim.py
|
|
23
|
+
# or as a package:
|
|
24
|
+
python3 -m src.run_sim
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Classroom Font Settings
|
|
28
|
+
|
|
29
|
+
The editor, machine-code listing, memory heatmap matrix, and register file use larger classroom-friendly defaults. You can also edit `config/ui_settings.json` before starting the simulator:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"base_font_size": 14,
|
|
34
|
+
"editor_font_size": 15,
|
|
35
|
+
"listing_font_size": 12,
|
|
36
|
+
"memory_font_size": 12,
|
|
37
|
+
"register_font_size": 16
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The editor, listing, and memory matrix also have `A-` / `A+` controls in the UI; those changes are saved back to the config file.
|
|
42
|
+
|
|
43
|
+
## Project Layout
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
src/
|
|
47
|
+
assembler/ TARA assembler
|
|
48
|
+
simulation/ CPU core and ISA behavior
|
|
49
|
+
ui/ PyQt application, panels, theme, dialogs
|
|
50
|
+
examples/ bundled example programs
|
|
51
|
+
assets/logo/ application logos
|
|
52
|
+
config/
|
|
53
|
+
memory/ memory CSV dumps
|
|
54
|
+
cpu_state/ machine-state JSON dumps
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Workflow
|
|
58
|
+
|
|
59
|
+
1. Write or load assembly code in the **editor** (left panel)
|
|
60
|
+
2. Click **⚙ Assemble & Load** — errors appear below the editor
|
|
61
|
+
3. Use **⏭ Step** to execute one instruction at a time
|
|
62
|
+
4. Or click **▶ Run** with a chosen clock frequency
|
|
63
|
+
5. Watch registers, memory, listing highlight, and the decode panel update in real time
|
|
64
|
+
6. Click **💾 Save Machine State** to write registers, PC, memory, counters, and source to JSON
|
|
65
|
+
7. Later click **⤴ Boot State** to restore the JSON state and continue computation
|
|
66
|
+
8. Click **↺ Reset** to reload the program and start over
|
|
67
|
+
|
|
68
|
+
## TARA ISA Quick Reference
|
|
69
|
+
|
|
70
|
+
| Format | Instructions | Encoding |
|
|
71
|
+
|--------|-------------|---------|
|
|
72
|
+
| F0 | NOP, HLT, RET | `op[15:11] 00000000000` |
|
|
73
|
+
| F1 | ADD, SUB, MUL, AND, OR, XOR, SLT | `op rd rsA rsB 00` |
|
|
74
|
+
| F2 | MOV, NOT | `op rd rs 00000` |
|
|
75
|
+
| F3 | LIL, LIH, ADDI, SHL, SHR | `op rd imm8` |
|
|
76
|
+
| F4 | LDW, STW, LDB, STB | `op rdata rbase off5` |
|
|
77
|
+
| F5 | BZ, BN | `op rtest rel8` |
|
|
78
|
+
| F6 | JMP, CALL | `op rel11` |
|
|
79
|
+
| F7 | PUSH, POP | `op rstk 00000000` |
|
|
80
|
+
|
|
81
|
+
## Memory Map
|
|
82
|
+
| Range | Use |
|
|
83
|
+
|-------|-----|
|
|
84
|
+
| 0x000–0x3FF | Program code |
|
|
85
|
+
| 0x400–0x4FF | Data / arrays |
|
|
86
|
+
| 0x500–0x5FE | Stack (R7) |
|
|
87
|
+
| 0x5FF | Display keyboard byte: bit0=left, bit1=right, bit2=up, bit3=down |
|
|
88
|
+
| 0x600–0x7FF | Display framebuffer, 64x64 pixels, 1 bit per pixel, 8 pixels per byte. Pixel values: 0 off, 1 on. |
|
|
89
|
+
|
|
90
|
+
## Register Conventions
|
|
91
|
+
| Reg | Convention |
|
|
92
|
+
|-----|-----------|
|
|
93
|
+
| R0–R5 | General purpose |
|
|
94
|
+
| R6 | Link register (CALL/RET) |
|
|
95
|
+
| R7 | Stack pointer (PUSH/POP) |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "taracpu"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "TARA CPU Simulator — 16-bit RISC teaching architecture (CS2300 at IIT Madras)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"PyQt5>=5.15",
|
|
14
|
+
"matplotlib>=3.5",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
taracpu = "taracpu:main"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["."]
|
|
22
|
+
include = ["src*", "taracpu*"]
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.package-data]
|
|
25
|
+
"src" = [
|
|
26
|
+
"assets/*",
|
|
27
|
+
"assets/logo/*",
|
|
28
|
+
"examples/progs/*.tara",
|
|
29
|
+
]
|
taracpu-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TARA Assembler
|
|
3
|
+
Converts TARA assembly text into 16-bit machine words.
|
|
4
|
+
Supports labels, comments (;), and all instruction formats.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from src.simulation.cpu import OP, FMT, sext
|
|
9
|
+
|
|
10
|
+
class AssemblerError(Exception):
|
|
11
|
+
def __init__(self, msg, line=None):
|
|
12
|
+
self.msg = msg
|
|
13
|
+
self.line = line
|
|
14
|
+
super().__init__(msg)
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return f"Line {self.line}: {self.msg}" if self.line else self.msg
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_reg(tok):
|
|
21
|
+
"""Parse Rn or rn → integer 0-7."""
|
|
22
|
+
tok = tok.strip().upper()
|
|
23
|
+
if tok.startswith('R') and tok[1:].isdigit():
|
|
24
|
+
n = int(tok[1:])
|
|
25
|
+
if 0 <= n <= 7:
|
|
26
|
+
return n
|
|
27
|
+
raise AssemblerError(f"Invalid register '{tok}'")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_int(tok, label_map=None, pc=None):
|
|
31
|
+
"""Parse a literal integer or label reference."""
|
|
32
|
+
tok = tok.strip()
|
|
33
|
+
if tok.startswith('0x') or tok.startswith('0X'):
|
|
34
|
+
return int(tok, 16)
|
|
35
|
+
if tok.startswith('0b') or tok.startswith('0B'):
|
|
36
|
+
return int(tok, 2)
|
|
37
|
+
if tok.lstrip('-').isdigit():
|
|
38
|
+
return int(tok)
|
|
39
|
+
if label_map is not None and tok in label_map:
|
|
40
|
+
return label_map[tok]
|
|
41
|
+
raise AssemblerError(f"Cannot parse value '{tok}'")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def parse_mem(tok):
|
|
45
|
+
"""Parse off(Rbase) → (offset_str, reg_str)."""
|
|
46
|
+
m = re.match(r'^(-?\d+|0x[0-9a-fA-F]+)\(([Rr]\d)\)$', tok.strip())
|
|
47
|
+
if not m:
|
|
48
|
+
raise AssemblerError(f"Invalid memory operand '{tok}'")
|
|
49
|
+
return m.group(1), m.group(2)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
OPERAND_COUNTS = {
|
|
53
|
+
'NOP': 0, 'HLT': 0, 'RET': 0,
|
|
54
|
+
'ADD': 3, 'SUB': 3, 'MUL': 3, 'AND': 3, 'OR': 3, 'XOR': 3, 'SLT': 3,
|
|
55
|
+
'MOV': 2, 'NOT': 2,
|
|
56
|
+
'LIL': 2, 'LIH': 2, 'ADDI': 2, 'SHL': 2, 'SHR': 2,
|
|
57
|
+
'LDW': 2, 'LDB': 2, 'STW': 2, 'STB': 2,
|
|
58
|
+
'BZ': 2, 'BN': 2,
|
|
59
|
+
'JMP': 1, 'CALL': 1,
|
|
60
|
+
'PUSH': 1, 'POP': 1,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _parse_operands(ops_str, mnem):
|
|
65
|
+
operands = [o.strip() for o in ops_str.split(',') if o.strip()]
|
|
66
|
+
expected = OPERAND_COUNTS[mnem]
|
|
67
|
+
if len(operands) != expected:
|
|
68
|
+
suffix = "" if expected == 1 else "s"
|
|
69
|
+
raise AssemblerError(
|
|
70
|
+
f"{mnem} expects {expected} operand{suffix}, got {len(operands)}"
|
|
71
|
+
)
|
|
72
|
+
return operands
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _label_rel(target_s, iaddr, label_map):
|
|
76
|
+
"""Resolve a branch/jump target to a signed word-offset relative to iaddr+2."""
|
|
77
|
+
if target_s in label_map:
|
|
78
|
+
return (label_map[target_s] - (iaddr + 2)) // 2
|
|
79
|
+
return parse_int(target_s, label_map)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _encode_f0(op):
|
|
83
|
+
return (op << 11)
|
|
84
|
+
|
|
85
|
+
def _encode_f1(op, rd, rA, rB):
|
|
86
|
+
return (op << 11) | (rd << 8) | (rA << 5) | (rB << 2)
|
|
87
|
+
|
|
88
|
+
def _encode_f2(op, rd, rs):
|
|
89
|
+
return (op << 11) | (rd << 8) | (rs << 5)
|
|
90
|
+
|
|
91
|
+
def _encode_f3(op, rd, imm8):
|
|
92
|
+
imm8 = imm8 & 0xFF
|
|
93
|
+
return (op << 11) | (rd << 8) | imm8
|
|
94
|
+
|
|
95
|
+
def _encode_f4(op, rdata, rbase, off5):
|
|
96
|
+
off5 = off5 & 0x1F
|
|
97
|
+
return (op << 11) | (rdata << 8) | (rbase << 5) | off5
|
|
98
|
+
|
|
99
|
+
def _encode_f5(op, rtest, rel8):
|
|
100
|
+
rel8 = rel8 & 0xFF
|
|
101
|
+
return (op << 11) | (rtest << 8) | rel8
|
|
102
|
+
|
|
103
|
+
def _encode_f6(op, rel11):
|
|
104
|
+
rel11 = rel11 & 0x7FF
|
|
105
|
+
return (op << 11) | rel11
|
|
106
|
+
|
|
107
|
+
def _encode_f7(op, rstk):
|
|
108
|
+
return (op << 11) | (rstk << 8)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def assemble(source):
|
|
112
|
+
"""
|
|
113
|
+
Assemble source text → (words, listing, errors)
|
|
114
|
+
words : list of (byte_addr, 16-bit int)
|
|
115
|
+
listing: list of dict per assembled instruction
|
|
116
|
+
errors : list of AssemblerError
|
|
117
|
+
"""
|
|
118
|
+
lines = source.splitlines()
|
|
119
|
+
errors = []
|
|
120
|
+
label_map = {} # label → byte address
|
|
121
|
+
parsed = [] # list of (lineno, addr, mnemonic, operands_str, original)
|
|
122
|
+
|
|
123
|
+
# ── Pass 1: strip comments, collect labels, count addresses ───────────────
|
|
124
|
+
addr = 0
|
|
125
|
+
for lineno, raw in enumerate(lines, 1):
|
|
126
|
+
line = raw.split(';')[0].strip()
|
|
127
|
+
if not line:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
# Extract label(s)
|
|
131
|
+
while ':' in line:
|
|
132
|
+
colon = line.index(':')
|
|
133
|
+
label = line[:colon].strip()
|
|
134
|
+
if label:
|
|
135
|
+
label_map[label] = addr
|
|
136
|
+
line = line[colon+1:].strip()
|
|
137
|
+
|
|
138
|
+
if not line:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
parts = line.split(None, 1)
|
|
142
|
+
mnem = parts[0].upper()
|
|
143
|
+
ops = parts[1].strip() if len(parts) > 1 else ''
|
|
144
|
+
|
|
145
|
+
if mnem not in OP:
|
|
146
|
+
errors.append(AssemblerError(f"Unknown mnemonic '{mnem}'", lineno))
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
parsed.append((lineno, addr, mnem, ops, raw.rstrip()))
|
|
150
|
+
addr += 2
|
|
151
|
+
|
|
152
|
+
# ── Pass 2: encode instructions ───────────────────────────────────────────
|
|
153
|
+
words = []
|
|
154
|
+
listing = []
|
|
155
|
+
|
|
156
|
+
for (lineno, iaddr, mnem, ops_str, original) in parsed:
|
|
157
|
+
opcode = OP[mnem]
|
|
158
|
+
word = 0
|
|
159
|
+
err = None
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
operands = _parse_operands(ops_str, mnem)
|
|
163
|
+
if mnem in ('NOP', 'HLT', 'RET'):
|
|
164
|
+
word = _encode_f0(opcode)
|
|
165
|
+
|
|
166
|
+
elif mnem in ('ADD', 'SUB', 'MUL', 'AND', 'OR', 'XOR', 'SLT'):
|
|
167
|
+
# rd, rA, rB
|
|
168
|
+
rd = parse_reg(operands[0])
|
|
169
|
+
rA = parse_reg(operands[1])
|
|
170
|
+
rB = parse_reg(operands[2])
|
|
171
|
+
word = _encode_f1(opcode, rd, rA, rB)
|
|
172
|
+
|
|
173
|
+
elif mnem in ('MOV', 'NOT'):
|
|
174
|
+
# rd, rs
|
|
175
|
+
rd = parse_reg(operands[0])
|
|
176
|
+
rs = parse_reg(operands[1])
|
|
177
|
+
word = _encode_f2(opcode, rd, rs)
|
|
178
|
+
|
|
179
|
+
elif mnem in ('LIL', 'LIH', 'ADDI'):
|
|
180
|
+
rd = parse_reg(operands[0])
|
|
181
|
+
imm = parse_int(operands[1], label_map)
|
|
182
|
+
word = _encode_f3(opcode, rd, imm)
|
|
183
|
+
|
|
184
|
+
elif mnem in ('SHL', 'SHR'):
|
|
185
|
+
# rd, shamt
|
|
186
|
+
rd = parse_reg(operands[0])
|
|
187
|
+
shamt = parse_int(operands[1], label_map)
|
|
188
|
+
word = _encode_f3(opcode, rd, shamt)
|
|
189
|
+
|
|
190
|
+
elif mnem in ('LDW', 'LDB'):
|
|
191
|
+
# rd, off(rbase)
|
|
192
|
+
rd = parse_reg(operands[0])
|
|
193
|
+
off_s, base_s = parse_mem(operands[1])
|
|
194
|
+
off = parse_int(off_s, label_map)
|
|
195
|
+
rbase = parse_reg(base_s)
|
|
196
|
+
word = _encode_f4(opcode, rd, rbase, off)
|
|
197
|
+
|
|
198
|
+
elif mnem in ('STW', 'STB'):
|
|
199
|
+
# rs, off(rbase)
|
|
200
|
+
rs = parse_reg(operands[0])
|
|
201
|
+
off_s, base_s = parse_mem(operands[1])
|
|
202
|
+
off = parse_int(off_s, label_map)
|
|
203
|
+
rbase = parse_reg(base_s)
|
|
204
|
+
word = _encode_f4(opcode, rs, rbase, off)
|
|
205
|
+
|
|
206
|
+
elif mnem in ('BZ', 'BN'):
|
|
207
|
+
rtest = parse_reg(operands[0])
|
|
208
|
+
rel = _label_rel(operands[1], iaddr, label_map)
|
|
209
|
+
word = _encode_f5(opcode, rtest, rel)
|
|
210
|
+
|
|
211
|
+
elif mnem in ('JMP', 'CALL'):
|
|
212
|
+
rel = _label_rel(operands[0], iaddr, label_map)
|
|
213
|
+
word = _encode_f6(opcode, rel)
|
|
214
|
+
|
|
215
|
+
elif mnem in ('PUSH', 'POP'):
|
|
216
|
+
rs = parse_reg(operands[0])
|
|
217
|
+
word = _encode_f7(opcode, rs)
|
|
218
|
+
|
|
219
|
+
else:
|
|
220
|
+
raise AssemblerError(f"Unhandled mnemonic '{mnem}'")
|
|
221
|
+
|
|
222
|
+
except (AssemblerError, ValueError, IndexError) as e:
|
|
223
|
+
if not isinstance(e, AssemblerError):
|
|
224
|
+
e = AssemblerError(str(e))
|
|
225
|
+
e.line = lineno
|
|
226
|
+
errors.append(e)
|
|
227
|
+
err = e
|
|
228
|
+
word = 0xDEAD
|
|
229
|
+
|
|
230
|
+
words.append((iaddr, word))
|
|
231
|
+
listing.append({
|
|
232
|
+
'lineno': lineno,
|
|
233
|
+
'addr': iaddr,
|
|
234
|
+
'word': word,
|
|
235
|
+
'mnem': mnem,
|
|
236
|
+
'ops': ops_str,
|
|
237
|
+
'original': original,
|
|
238
|
+
'error': bool(err),
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
return words, listing, errors, label_map
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from src.paths import PROGS_DIR
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _display_name(path):
|
|
7
|
+
return path.stem.replace('_', ' ').title()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_examples():
|
|
11
|
+
examples = {}
|
|
12
|
+
progs = Path(PROGS_DIR)
|
|
13
|
+
if not progs.exists():
|
|
14
|
+
return examples
|
|
15
|
+
for path in sorted(progs.glob('*.tara')):
|
|
16
|
+
examples[_display_name(path)] = path.read_text(encoding='utf-8', errors='replace')
|
|
17
|
+
return examples
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
; Binary search for a sorted byte array.
|
|
2
|
+
;
|
|
3
|
+
; Interface at label "search":
|
|
4
|
+
; R0 = starting byte address of sorted array
|
|
5
|
+
; R1 = number of elements
|
|
6
|
+
; R2 = search key
|
|
7
|
+
;
|
|
8
|
+
; Return value:
|
|
9
|
+
; R3 = index of matching element, or 0xFFFF if not found
|
|
10
|
+
;
|
|
11
|
+
; The demo setup below searches for 7 in [1,3,5,7,9,11] at 0x0400.
|
|
12
|
+
; After HLT, R3 contains 3.
|
|
13
|
+
|
|
14
|
+
; ---- Demo setup -----------------------------------------------------
|
|
15
|
+
LIL R0, 0
|
|
16
|
+
LIH R0, 4 ; R0 = 0x0400
|
|
17
|
+
LIL R1, 6 ; N = 6 byte elements
|
|
18
|
+
LIL R2, 7 ; key = 7
|
|
19
|
+
|
|
20
|
+
LIL R3, 1
|
|
21
|
+
STB R3, 0(R0)
|
|
22
|
+
LIL R3, 3
|
|
23
|
+
STB R3, 1(R0)
|
|
24
|
+
LIL R3, 5
|
|
25
|
+
STB R3, 2(R0)
|
|
26
|
+
LIL R3, 7
|
|
27
|
+
STB R3, 3(R0)
|
|
28
|
+
LIL R3, 9
|
|
29
|
+
STB R3, 4(R0)
|
|
30
|
+
LIL R3, 11
|
|
31
|
+
STB R3, 5(R0)
|
|
32
|
+
|
|
33
|
+
; ---- Binary search --------------------------------------------------
|
|
34
|
+
; R3 = result index
|
|
35
|
+
; R4 = low index
|
|
36
|
+
; R5 = high index
|
|
37
|
+
; R6 = mid index
|
|
38
|
+
; R7 = scratch / loaded array value
|
|
39
|
+
|
|
40
|
+
search: LIL R3, 255
|
|
41
|
+
LIH R3, 255 ; default result = 0xFFFF (not found)
|
|
42
|
+
LIL R4, 0 ; low = 0
|
|
43
|
+
MOV R5, R1
|
|
44
|
+
ADDI R5, -1 ; high = N - 1
|
|
45
|
+
|
|
46
|
+
loop: SUB R7, R5, R4
|
|
47
|
+
BN R7, done ; high < low
|
|
48
|
+
|
|
49
|
+
MOV R6, R4
|
|
50
|
+
ADD R6, R6, R5
|
|
51
|
+
SHR R6, 1 ; mid = (low + high) / 2
|
|
52
|
+
|
|
53
|
+
ADD R7, R0, R6
|
|
54
|
+
LDB R7, 0(R7) ; R7 = A[mid]
|
|
55
|
+
SUB R7, R7, R2 ; compare A[mid] - key
|
|
56
|
+
|
|
57
|
+
BZ R7, found
|
|
58
|
+
BN R7, go_right ; A[mid] < key
|
|
59
|
+
|
|
60
|
+
MOV R5, R6 ; A[mid] > key, high = mid - 1
|
|
61
|
+
ADDI R5, -1
|
|
62
|
+
JMP loop
|
|
63
|
+
|
|
64
|
+
go_right:
|
|
65
|
+
MOV R4, R6 ; low = mid + 1
|
|
66
|
+
ADDI R4, 1
|
|
67
|
+
JMP loop
|
|
68
|
+
|
|
69
|
+
found: MOV R3, R6
|
|
70
|
+
|
|
71
|
+
done: HLT
|