mos-tui 1.0.1 → 1.0.2
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.
- package/core/OS_CP.c +6 -2
- package/core/mos.sh +16 -17
- package/core/mos_tui.py +108 -53
- package/output.txt +0 -0
- package/package.json +1 -1
package/core/OS_CP.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include<stdio.h>
|
|
2
2
|
#include <stdbool.h>
|
|
3
3
|
#include<time.h>
|
|
4
|
+
#include <stdlib.h>
|
|
4
5
|
|
|
5
6
|
// Declaring the required Memory and Registers
|
|
6
7
|
char M[100][4]; // main storage of size 100 x 4
|
|
@@ -261,8 +262,11 @@ int main() {
|
|
|
261
262
|
|
|
262
263
|
start = clock();
|
|
263
264
|
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
char *infile = getenv("MOS_INPUT");
|
|
266
|
+
char *outfile = getenv("MOS_OUTPUT");
|
|
267
|
+
|
|
268
|
+
fin = fopen(infile ? infile : "input.txt", "r");
|
|
269
|
+
fout = fopen(outfile ? outfile : "output.txt", "w");
|
|
266
270
|
|
|
267
271
|
if (fin == NULL) {
|
|
268
272
|
printf("Input file not found\n");
|
package/core/mos.sh
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# ─────────────────────────────────────────────────────────────
|
|
3
|
+
# MOS Terminal UI Launcher
|
|
4
|
+
# Usage: ./mos.sh
|
|
5
|
+
# Run from the same directory as OS_CP.c
|
|
6
|
+
# ─────────────────────────────────────────────────────────────
|
|
2
7
|
set -e
|
|
3
8
|
|
|
4
9
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
10
|
TUI="$SCRIPT_DIR/mos_tui.py"
|
|
6
|
-
|
|
11
|
+
PROJECT_DIR="$(pwd)" # where the user IS standing — must have OS_CP.c
|
|
7
12
|
|
|
8
13
|
# Check Python 3
|
|
9
14
|
if ! command -v python3 &>/dev/null; then
|
|
10
|
-
echo "python3 not found. Install
|
|
15
|
+
echo "python3 not found. Install: sudo apt install python3"
|
|
11
16
|
exit 1
|
|
12
17
|
fi
|
|
13
18
|
|
|
14
19
|
# Check gcc
|
|
15
20
|
if ! command -v gcc &>/dev/null; then
|
|
16
|
-
echo "gcc not found. Install
|
|
21
|
+
echo "gcc not found. Install: sudo apt install gcc"
|
|
17
22
|
exit 1
|
|
18
23
|
fi
|
|
19
24
|
|
|
20
|
-
#
|
|
21
|
-
if [ -f "OS_CP.c" ]; then
|
|
22
|
-
|
|
23
|
-
echo "
|
|
24
|
-
else
|
|
25
|
-
OS_FILE="$DEFAULT_OS"
|
|
26
|
-
echo "📦 Using bundled OS_CP.c"
|
|
25
|
+
# Warn if OS_CP.c is missing
|
|
26
|
+
if [ ! -f "$PROJECT_DIR/OS_CP.c" ]; then
|
|
27
|
+
echo "WARNING: OS_CP.c not found in: $PROJECT_DIR"
|
|
28
|
+
echo " cd into the folder that contains OS_CP.c, then run mos.sh again."
|
|
27
29
|
fi
|
|
28
30
|
|
|
29
|
-
# Export so Python can use it
|
|
30
|
-
export MOS_OS_FILE="$OS_FILE"
|
|
31
|
-
|
|
32
|
-
# Ensure the TUI script exists
|
|
33
31
|
if [ ! -f "$TUI" ]; then
|
|
34
32
|
echo "mos_tui.py not found at: $TUI"
|
|
35
33
|
exit 1
|
|
36
34
|
fi
|
|
37
35
|
|
|
38
|
-
# Terminal setup
|
|
39
36
|
export TERM="${TERM:-xterm-256color}"
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
# cd into the project dir so Python's os.getcwd() == where OS_CP.c is
|
|
39
|
+
cd "$PROJECT_DIR"
|
|
43
40
|
|
|
41
|
+
echo "Starting MOS Terminal UI..."
|
|
42
|
+
sleep 0.2
|
|
44
43
|
python3 "$TUI"
|
package/core/mos_tui.py
CHANGED
|
@@ -28,18 +28,45 @@ C_INPUT = 9 # green on black (input text)
|
|
|
28
28
|
C_ORANGE = 10 # yellow bright (used for EXEC tags)
|
|
29
29
|
C_STAT_VAL = 11 # cyan bold for stat values
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
31
|
+
# Locate OS_CP.c by searching:
|
|
32
|
+
# 1. Directory explicitly set via MOS_SOURCE env var
|
|
33
|
+
# 2. Current working directory
|
|
34
|
+
# 3. Walk UP from cwd (handles npm package subdirectory installs)
|
|
35
|
+
# 4. Walk UP from script location
|
|
36
|
+
def _find_source():
|
|
37
|
+
# Env override — highest priority
|
|
38
|
+
env_src = os.environ.get("MOS_SOURCE")
|
|
39
|
+
if env_src and os.path.exists(env_src):
|
|
40
|
+
return os.path.abspath(env_src)
|
|
41
|
+
|
|
42
|
+
# Search upward from cwd
|
|
43
|
+
search = os.getcwd()
|
|
44
|
+
for _ in range(6):
|
|
45
|
+
candidate = os.path.join(search, "OS_CP.c")
|
|
46
|
+
if os.path.exists(candidate):
|
|
47
|
+
return candidate
|
|
48
|
+
parent = os.path.dirname(search)
|
|
49
|
+
if parent == search:
|
|
50
|
+
break
|
|
51
|
+
search = parent
|
|
52
|
+
|
|
53
|
+
# Search upward from script location
|
|
54
|
+
search = os.path.dirname(os.path.abspath(__file__))
|
|
55
|
+
for _ in range(6):
|
|
56
|
+
candidate = os.path.join(search, "OS_CP.c")
|
|
57
|
+
if os.path.exists(candidate):
|
|
58
|
+
return candidate
|
|
59
|
+
parent = os.path.dirname(search)
|
|
60
|
+
if parent == search:
|
|
61
|
+
break
|
|
62
|
+
search = parent
|
|
39
63
|
|
|
40
|
-
#
|
|
41
|
-
|
|
64
|
+
# Fallback — cwd (will show a clear error)
|
|
65
|
+
return os.path.join(os.getcwd(), "OS_CP.c")
|
|
42
66
|
|
|
67
|
+
SOURCE = _find_source()
|
|
68
|
+
_CWD = os.path.dirname(SOURCE) # always the folder that contains OS_CP.c
|
|
69
|
+
BINARY = os.path.join(_CWD, "mos_bin")
|
|
43
70
|
|
|
44
71
|
# ── STATE ─────────────────────────────────────────────────────────────────────
|
|
45
72
|
class State:
|
|
@@ -74,15 +101,15 @@ state = State()
|
|
|
74
101
|
# ── COMPILE ───────────────────────────────────────────────────────────────────
|
|
75
102
|
def compile_source():
|
|
76
103
|
if not os.path.exists(SOURCE):
|
|
77
|
-
state.
|
|
78
|
-
state.add_debug("
|
|
104
|
+
state.add_debug("ERR", f"OS_CP.c not found at: {SOURCE}")
|
|
105
|
+
state.add_debug("WARN", f"Run this tool from the folder containing OS_CP.c")
|
|
106
|
+
state.add_debug("WARN", f"Current dir: {_CWD}")
|
|
79
107
|
return False
|
|
80
108
|
state.add_debug("INFO", f"Compiling {SOURCE}...")
|
|
81
109
|
result = subprocess.run(
|
|
82
110
|
["gcc", "-o", BINARY, SOURCE, "-lm"],
|
|
83
|
-
cwd=
|
|
84
|
-
capture_output=True,
|
|
85
|
-
text=True
|
|
111
|
+
cwd=_CWD,
|
|
112
|
+
capture_output=True, text=True
|
|
86
113
|
)
|
|
87
114
|
if result.returncode != 0:
|
|
88
115
|
state.compile_error = result.stderr.strip()
|
|
@@ -99,24 +126,32 @@ def run_program(input_text, callback):
|
|
|
99
126
|
state.status = "RUNNING"
|
|
100
127
|
state.add_debug("INFO", f"─── Run #{state.run_count} started ───")
|
|
101
128
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
# Use a dedicated temp directory so input.txt and output.txt are isolated
|
|
130
|
+
run_dir = tempfile.mkdtemp(prefix='mos_run_')
|
|
131
|
+
fin_path = os.path.join(run_dir, "input.txt")
|
|
132
|
+
fout_path = os.path.join(run_dir, "output.txt")
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
with open(fin_path, 'w') as f:
|
|
135
|
+
f.write(input_text)
|
|
136
|
+
|
|
137
|
+
state.add_debug("INFO", f"Input written → {fin_path}")
|
|
107
138
|
|
|
108
139
|
t0 = time.perf_counter()
|
|
109
140
|
try:
|
|
110
141
|
result = subprocess.run(
|
|
111
142
|
[BINARY],
|
|
112
|
-
stdin=open(fin_path),
|
|
113
143
|
capture_output=True,
|
|
114
144
|
text=True,
|
|
115
145
|
timeout=10,
|
|
116
|
-
|
|
146
|
+
cwd=run_dir, # binary runs inside temp dir
|
|
147
|
+
env={**os.environ,
|
|
148
|
+
'MOS_INPUT': fin_path, # env var override (your updated C uses this)
|
|
149
|
+
'MOS_OUTPUT': fout_path}
|
|
117
150
|
)
|
|
118
151
|
elapsed_ms = (time.perf_counter() - t0) * 1000
|
|
119
152
|
|
|
153
|
+
state.add_debug("INFO", f"Process exited (code={result.returncode})")
|
|
154
|
+
|
|
120
155
|
# Parse stdout debug lines from the C program
|
|
121
156
|
for line in result.stdout.strip().splitlines():
|
|
122
157
|
line = line.strip()
|
|
@@ -143,20 +178,32 @@ def run_program(input_text, callback):
|
|
|
143
178
|
for line in result.stderr.strip().splitlines():
|
|
144
179
|
state.add_debug("ERR", line)
|
|
145
180
|
|
|
146
|
-
# Read output.txt
|
|
181
|
+
# Read output.txt from the run directory (guaranteed location)
|
|
147
182
|
output_content = []
|
|
148
|
-
if os.path.exists(
|
|
149
|
-
with open("output.txt") as f:
|
|
150
|
-
output_content = f.read().splitlines()
|
|
151
|
-
elif os.path.exists(fout_path):
|
|
183
|
+
if os.path.exists(fout_path):
|
|
152
184
|
with open(fout_path) as f:
|
|
153
185
|
output_content = f.read().splitlines()
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
state.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
state.add_debug("OK", f"Output read from {fout_path} ({len(output_content)} lines)")
|
|
187
|
+
else:
|
|
188
|
+
state.add_debug("WARN", f"output.txt not created at {fout_path}")
|
|
189
|
+
|
|
190
|
+
# Debug: show raw repr of each line so we can see spaces vs real content
|
|
191
|
+
for idx, line in enumerate(output_content):
|
|
192
|
+
stripped = line.rstrip()
|
|
193
|
+
if stripped:
|
|
194
|
+
state.add_output(stripped)
|
|
195
|
+
else:
|
|
196
|
+
# Don't add pure-blank lines from TERMINATE()'s \n\n — skip them
|
|
197
|
+
# but log them so user can see in debug
|
|
198
|
+
state.add_debug("INFO", f"output line {idx+1}: (blank/whitespace only)")
|
|
199
|
+
|
|
200
|
+
if result.returncode == 0:
|
|
201
|
+
if not output_content:
|
|
202
|
+
state.add_output("[output.txt was empty — check your PD instruction address]")
|
|
203
|
+
elif not any(l.strip() for l in output_content):
|
|
204
|
+
state.add_output("[output.txt had only blank lines — PD address may point to empty memory]")
|
|
205
|
+
state.add_debug("WARN", "All output lines were blank — GD and PD addresses must match")
|
|
206
|
+
state.add_debug("WARN", "e.g. GD10 reads into addr 10-19, so use PD10 to print it")
|
|
160
207
|
|
|
161
208
|
state.exec_time_ms = elapsed_ms
|
|
162
209
|
state.run_history.append((f"Run #{state.run_count}", elapsed_ms))
|
|
@@ -170,7 +217,8 @@ def run_program(input_text, callback):
|
|
|
170
217
|
state.add_debug("ERR", f"Runner error: {e}")
|
|
171
218
|
state.status = "ERROR"
|
|
172
219
|
finally:
|
|
173
|
-
|
|
220
|
+
import shutil
|
|
221
|
+
try: shutil.rmtree(run_dir, ignore_errors=True)
|
|
174
222
|
except: pass
|
|
175
223
|
|
|
176
224
|
callback()
|
|
@@ -312,10 +360,14 @@ def draw_output_panel(win, active):
|
|
|
312
360
|
fill_line(win, y)
|
|
313
361
|
if li < len(lines):
|
|
314
362
|
line = lines[li]
|
|
363
|
+
# line number gutter
|
|
364
|
+
gutter = f"{li+1:3} "
|
|
365
|
+
safe_addstr(win, y, 1, gutter, curses.color_pair(C_DIM))
|
|
315
366
|
col = curses.color_pair(C_GREEN)
|
|
316
367
|
if not line.strip():
|
|
317
|
-
|
|
318
|
-
|
|
368
|
+
safe_addstr(win, y, 5, "~", curses.color_pair(C_DIM))
|
|
369
|
+
else:
|
|
370
|
+
safe_addstr(win, y, 5, line[:inner_w-5], col)
|
|
319
371
|
|
|
320
372
|
# ── PANEL: CHART ──────────────────────────────────────────────────────────────
|
|
321
373
|
SPARK_CHARS = " ▁▂▃▄▅▆▇█"
|
|
@@ -446,33 +498,34 @@ def draw_titlebar(stdscr, W):
|
|
|
446
498
|
|
|
447
499
|
# ── SAMPLE PROGRAMS ───────────────────────────────────────────────────────────
|
|
448
500
|
SAMPLES = [
|
|
501
|
+
# Sample 1: Read data into addr 10, print from addr 10
|
|
449
502
|
"""$AMJ
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
SR30
|
|
453
|
-
LR30
|
|
454
|
-
PD30
|
|
503
|
+
GD10
|
|
504
|
+
PD10
|
|
455
505
|
H
|
|
456
506
|
$DTA
|
|
457
|
-
Hello MOS!
|
|
507
|
+
Hello MOS!
|
|
458
508
|
$END""",
|
|
509
|
+
# Sample 2: Read two lines, print both
|
|
459
510
|
"""$AMJ
|
|
460
511
|
GD10
|
|
461
512
|
PD10
|
|
462
|
-
|
|
463
|
-
|
|
513
|
+
GD30
|
|
514
|
+
PD30
|
|
464
515
|
H
|
|
465
516
|
$DTA
|
|
466
|
-
First
|
|
467
|
-
Second
|
|
517
|
+
First Line
|
|
518
|
+
Second Line
|
|
468
519
|
$END""",
|
|
520
|
+
# Sample 3: Load, store, compare, branch
|
|
469
521
|
"""$AMJ
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
522
|
+
GD10
|
|
523
|
+
LR10
|
|
524
|
+
SR20
|
|
525
|
+
PD20
|
|
474
526
|
H
|
|
475
527
|
$DTA
|
|
528
|
+
Stored Data
|
|
476
529
|
$END""",
|
|
477
530
|
]
|
|
478
531
|
_sample_idx = [0]
|
|
@@ -585,12 +638,12 @@ def main(stdscr):
|
|
|
585
638
|
stdscr.keypad(True)
|
|
586
639
|
|
|
587
640
|
state.add_debug("INFO", "MOS Terminal UI started")
|
|
588
|
-
state.add_debug("INFO", f"
|
|
641
|
+
state.add_debug("INFO", f"Working dir: {_CWD}")
|
|
589
642
|
if os.path.exists(SOURCE):
|
|
590
|
-
state.add_debug("OK", f"Found
|
|
643
|
+
state.add_debug("OK", f"Found OS_CP.c — press F6 to compile")
|
|
591
644
|
else:
|
|
592
|
-
state.add_debug("
|
|
593
|
-
state.add_debug("WARN", "
|
|
645
|
+
state.add_debug("ERR", f"OS_CP.c not found in: {_CWD}")
|
|
646
|
+
state.add_debug("WARN", "cd into your project folder, then rerun ./mos.sh")
|
|
594
647
|
|
|
595
648
|
load_sample()
|
|
596
649
|
|
|
@@ -687,6 +740,8 @@ def main(stdscr):
|
|
|
687
740
|
state.debug_scroll = 0
|
|
688
741
|
|
|
689
742
|
def done():
|
|
743
|
+
state.focus = 2 # auto-switch to Output panel
|
|
744
|
+
state.output_scroll = 0 # scroll to top of output
|
|
690
745
|
redraw()
|
|
691
746
|
|
|
692
747
|
t = threading.Thread(target=run_program, args=(input_text, done), daemon=True)
|
package/output.txt
ADDED
|
File without changes
|