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 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
- fin = fopen("input.txt", "r");
265
- fout = fopen("output.txt", "w");
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
- DEFAULT_OS="$SCRIPT_DIR/OS_CP.c"
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 it with: sudo apt install python3"
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 it with: sudo apt install gcc"
21
+ echo "gcc not found. Install: sudo apt install gcc"
17
22
  exit 1
18
23
  fi
19
24
 
20
- # Decide which OS_CP.c to use
21
- if [ -f "OS_CP.c" ]; then
22
- OS_FILE="$(pwd)/OS_CP.c"
23
- echo "📄 Using OS_CP.c from current directory"
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
- echo "🚀 Starting MOS Terminal UI..."
42
- sleep 0.3
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
- # Correct base directory (where this script is installed)
32
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
33
-
34
- # Get source file (user provided OR bundled)
35
- SOURCE = os.environ.get(
36
- "MOS_OS_FILE",
37
- os.path.join(BASE_DIR, "OS_CP.c")
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
- # Binary will be created in same directory as source
41
- BINARY = os.path.join(os.path.dirname(SOURCE), "mos_bin")
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.compile_error = f"'{SOURCE}' not found in current directory."
78
- state.add_debug("ERR", f"Source file {SOURCE} not found")
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=os.path.dirname(SOURCE),
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
- with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, prefix='mos_in_') as fin:
103
- fin.write(input_text)
104
- fin_path = fin.name
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
- fout_path = fin_path.replace('mos_in_', 'mos_out_').replace('.txt', '_out.txt')
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
- env={**os.environ, 'MOS_INPUT': fin_path, 'MOS_OUTPUT': fout_path}
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 if it was produced
181
+ # Read output.txt from the run directory (guaranteed location)
147
182
  output_content = []
148
- if os.path.exists("output.txt"):
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
- for line in output_content:
156
- state.add_output(line)
157
-
158
- if not output_content and result.returncode == 0:
159
- state.add_output("[No output written check PD instruction addresses]")
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
- try: os.unlink(fin_path)
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
- col = curses.color_pair(C_DIM)
318
- safe_addstr(win, y, 1, line[:inner_w], col)
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
- LR10
451
- GD20
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
- GD20
463
- PD20
513
+ GD30
514
+ PD30
464
515
  H
465
516
  $DTA
466
- First Data
467
- Second Data
517
+ First Line
518
+ Second Line
468
519
  $END""",
520
+ # Sample 3: Load, store, compare, branch
469
521
  """$AMJ
470
- LR05
471
- CR05
472
- BT07
473
- PD05
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"Using source: {SOURCE}")
641
+ state.add_debug("INFO", f"Working dir: {_CWD}")
589
642
  if os.path.exists(SOURCE):
590
- state.add_debug("OK", f"Found {SOURCE} — press F6 to compile")
643
+ state.add_debug("OK", f"Found OS_CP.c — press F6 to compile")
591
644
  else:
592
- state.add_debug("WARN", f"{SOURCE} not found in current directory")
593
- state.add_debug("WARN", "Place OS_CP.c here and press F6")
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mos-tui",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "MOS Terminal UI Launcher",
5
5
  "bin": {
6
6
  "mos": "./bin/mos.js"