shithub: orca

Download patch

ref: 8badbba4c17d4152ceef4222a72e62309c0cfa5c
parent: ab94883dcfe54c3f6e93e96e7a70cc254b8415eb
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Apr 30 17:46:25 EDT 2020

plan9: show help message on the current cell

--- a/base.h
+++ b/base.h
@@ -7,6 +7,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
--- a/plan9.c
+++ b/plan9.c
@@ -82,6 +82,7 @@
 static int midi = -1;
 static u8int noteoff[16][128]; /* 16 channels, 128 notes each */
 
+static Help help;
 static Rune *linebuf;
 static Rune cursor = '@';
 static vlong tick;
@@ -729,8 +730,9 @@
 	}
 
 	i = 0;
+	i += runesprint(linebuf, "%-10s", help.name);
 	sprint(s, "%udx%ud", field.width, field.height);
-	i += runesprint(linebuf, "%-10s", s);
+	i += runesprint(linebuf+i, "%-10s", s);
 	sprint(s, "%d/%d", rulers.x, rulers.y);
 	i += runesprint(linebuf+i, "%-9s", s);
 	sprint(s, "%lldf%c", MAX(0, tick), pause ? '~' : 0);
@@ -746,8 +748,9 @@
 	bot.y += glyphsz.y;
 
 	i = 0;
+	i += runesprint(linebuf, "%-10s", "");
 	sprint(s, "%ud,%ud", cur.x, cur.y);
-	i += runesprint(linebuf, "%-10s", s);
+	i += runesprint(linebuf+i, "%-10s", s);
 	sprint(s, "%d:%d", sel.min.x < cur.x ? -Dx(sel) : Dx(sel), sel.max.x < cur.x ? -Dy(sel) : Dy(sel));
 	i += runesprint(linebuf+i, "%-9s", s);
 	i += runesprint(linebuf+i, "%-9s", modes[altdown ? Mslide : mode]);
@@ -992,7 +995,9 @@
 		h = field.height;
 		mbuffer_clear(mbuf.buffer, h, w);
 		oevent_list_clear(&events);
-		orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0);
+		help.x = cur.x;
+		help.y = cur.y;
+		orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0, &help);
 
 		processold = processnew;
 		processnew = nanosec();
--- a/plan9.h
+++ b/plan9.h
@@ -3,6 +3,8 @@
 #include <u.h>
 #include <libc.h>
 
+#define sprintf sprint
+
 typedef enum { false, true } bool;
 #define ORCA_NOINLINE
 #define ORCA_FORCEINLINE
--- a/sim.c
+++ b/sim.c
@@ -100,7 +100,7 @@
       Glyph *const restrict gbuffer, Mark *const restrict mbuffer,             \
       Usz const height, Usz const width, Usz const y, Usz const x,             \
       Usz Tick_number, Oper_extra_params *const extra_params,                  \
-      Mark const cell_flags, Glyph const This_oper_char) {                     \
+      Mark const cell_flags, Glyph const This_oper_char, Help *help) {         \
     (void)gbuffer;                                                             \
     (void)mbuffer;                                                             \
     (void)height;                                                              \
@@ -110,7 +110,8 @@
     (void)Tick_number;                                                         \
     (void)extra_params;                                                        \
     (void)cell_flags;                                                          \
-    (void)This_oper_char;
+    (void)This_oper_char;                                                      \
+    (void)help;
 
 #define END_OPERATOR }
 
@@ -143,49 +144,52 @@
   if (!oper_has_neighboring_bang(gbuffer, height, width, y, x))                \
   return
 
-#define PORT(_delta_y, _delta_x, _flags)                                       \
+#define PORT(_delta_y, _delta_x, _flags, _name)                                \
+  if (x+_delta_x == help->x && y+_delta_y == help->y) {                        \
+    sprintf(help->name, "%c-%s", This_oper_char, _name);                       \
+  }                                                                            \
   mbuffer_poke_relative_flags_or(mbuffer, height, width, y, x, _delta_y,       \
                                  _delta_x, (_flags) ^ Mark_flag_lock)
 //////// Operators
 
 #define UNIQUE_OPERATORS(_)                                                    \
-  _('!', midicc)                                                               \
-  _('#', comment)                                                              \
-  _('%', midi)                                                                 \
-  _('*', bang)                                                                 \
-  _(':', midi)                                                                 \
-  _(';', udp)                                                                  \
-  _('=', osc)                                                                  \
-  _('?', midipb)                                                               \
-  _('$', cmd)
+  _('!', midicc, "cc", "Sends MIDI control change")                            \
+  _('#', comment, "comment", "Halts line")                                     \
+  _('%', midi, "mono", "Sends MIDI monophonic note")                           \
+  _('*', bang, "bang", "Bangs neighboring operands")                           \
+  _(':', midi, "midi", "Sends MIDI note")                                      \
+  _(';', udp, "udp", "Sends UDP message")                                      \
+  _('=', osc, "osc", "Sends OSC message")                                      \
+  _('?', midipb, "pb", "Sends MIDI pitch bend")                                \
+  _('$', cmd, "self", "Sends ORCA command")
 
 #define ALPHA_OPERATORS(_)                                                     \
-  _('A', add)                                                                  \
-  _('B', bounce)                                                               \
-  _('C', clock)                                                                \
-  _('D', delay)                                                                \
-  _('E', movement)                                                             \
-  _('F', if)                                                                   \
-  _('G', generator)                                                            \
-  _('H', halt)                                                                 \
-  _('I', increment)                                                            \
-  _('J', jump)                                                                 \
-  _('K', konkat)                                                               \
-  _('L', lesser)                                                               \
-  _('M', multiply)                                                             \
-  _('N', movement)                                                             \
-  _('O', offset)                                                               \
-  _('P', push)                                                                 \
-  _('Q', query)                                                                \
-  _('R', random)                                                               \
-  _('S', movement)                                                             \
-  _('T', track)                                                                \
-  _('U', uclid)                                                                \
-  _('V', variable)                                                             \
-  _('W', movement)                                                             \
-  _('X', teleport)                                                             \
-  _('Y', yump)                                                                 \
-  _('Z', lerp)
+  _('A', add, "add", "Outputs sum of inputs")                                  \
+  _('B', between, "between", "Outputs difference of inputs")                   \
+  _('C', clock, "clock", "Outputs modulo of frame")                            \
+  _('D', delay, "delay", "Bangs on modulo of frame")                           \
+  _('E', movement, "east", "Moves eastward, or bangs")                         \
+  _('F', if, "if", "Bangs if inputs are equal")                                \
+  _('G', generator, "generator", "Writes operands with offset")                \
+  _('H', halt, "halt", "Halts southward operand")                              \
+  _('I', increment, "increment", "Increments southward operand")               \
+  _('J', jump, "jumper", "Outputs northward operand")                          \
+  _('K', konkat, "konkat", "Reads multiple variables")                         \
+  _('L', lesser, "lesser", "Outputs smallest input")                           \
+  _('M', multiply, "multiply", "Outputs product of inputs")                    \
+  _('N', movement, "north", "Moves Northward, or bangs")                       \
+  _('O', offset, "read", "Reads operand with offset")                          \
+  _('P', push, "push", "Writes eastward operand")                              \
+  _('Q', query, "query", "Reads operands with offset")                         \
+  _('R', random, "random", "Outputs random value")                             \
+  _('S', movement, "south", "Moves southward, or bangs")                       \
+  _('T', track, "track", "Reads eastward operand")                             \
+  _('U', uclid, "uclid", "Bangs on Euclidean rhythm")                          \
+  _('V', variable, "variable", "Reads and writes variable")                    \
+  _('W', movement, "west", "Moves westward, or bangs")                         \
+  _('X', teleport, "write", "Writes operand with offset")                      \
+  _('Y', yump, "jymper", "Outputs westward operand")                           \
+  _('Z', lerp, "lerp", "Transitions operand to target")
 
 BEGIN_OPERATOR(movement)
   if (glyph_is_lowercase(This_oper_char) &&
@@ -232,9 +236,9 @@
 END_OPERATOR
 
 BEGIN_OPERATOR(midicc)
-  for (Usz i = 1; i < 4; ++i) {
-    PORT(0, (Isz)i, IN);
-  }
+  PORT(0, 1, IN, "channel");
+  PORT(0, 2, IN, "knob");
+  PORT(0, 3, IN, "value");
   STOP_IF_NOT_BANGED;
   Glyph channel_g = PEEK(0, 1);
   Glyph control_g = PEEK(0, 2);
@@ -272,9 +276,11 @@
 END_OPERATOR
 
 BEGIN_OPERATOR(midi)
-  for (Usz i = 1; i < 6; ++i) {
-    PORT(0, (Isz)i, IN);
-  }
+  PORT(0, 1, IN, "channel");
+  PORT(0, 2, IN, "octave");
+  PORT(0, 3, IN, "note");
+  PORT(0, 4, IN, "velocity");
+  PORT(0, 5, IN, "length");
   STOP_IF_NOT_BANGED;
   Glyph channel_g = PEEK(0, 1);
   Glyph octave_g = PEEK(0, 2);
@@ -347,13 +353,13 @@
 END_OPERATOR
 
 BEGIN_OPERATOR(osc)
-  PORT(0, 1, IN | PARAM);
-  PORT(0, 2, IN | PARAM);
+  PORT(0, 1, IN | PARAM, "path");
+  PORT(0, 2, IN | PARAM, "len");
   Usz len = index_of(PEEK(0, 2));
   if (len > Oevent_osc_int_count)
     len = Oevent_osc_int_count;
   for (Usz i = 0; i < len; ++i) {
-    PORT(0, (Isz)i + 3, IN);
+    PORT(0, (Isz)i + 3, IN, "in");
   }
   STOP_IF_NOT_BANGED;
   Glyph g = PEEK(0, 1);
@@ -374,9 +380,9 @@
 END_OPERATOR
 
 BEGIN_OPERATOR(midipb)
-  for (Usz i = 1; i < 4; ++i) {
-    PORT(0, (Isz)i, IN);
-  }
+  PORT(0, 1, IN, "channel");
+  PORT(0, 2, IN, "lsb");
+  PORT(0, 3, IN, "msb");
   STOP_IF_NOT_BANGED;
   Glyph channel_g = PEEK(0, 1);
   Glyph msb_g = PEEK(0, 2);
@@ -422,9 +428,9 @@
 
 BEGIN_OPERATOR(add)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "a");
+  PORT(0, 1, IN, "b");
+  PORT(1, 0, OUT, "output");
   Glyph a = PEEK(0, -1);
   Glyph b = PEEK(0, 1);
   Glyph g = glyph_table[(index_of(a) + index_of(b)) % Glyphs_index_count];
@@ -431,11 +437,11 @@
   POKE(1, 0, glyph_with_case(g, b));
 END_OPERATOR
 
-BEGIN_OPERATOR(bounce)
+BEGIN_OPERATOR(between)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "a");
+  PORT(0, 1, IN, "b");
+  PORT(1, 0, OUT, "output");
   Glyph a = PEEK(0, -1);
   Glyph b = PEEK(0, 1);
   Isz val = (Isz)index_of(b) - (Isz)index_of(a);
@@ -446,9 +452,9 @@
 
 BEGIN_OPERATOR(clock)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "rate");
+  PORT(0, 1, IN, "mod");
+  PORT(1, 0, OUT, "output");
   Usz rate = index_of(PEEK(0, -1));
   Usz mod_num = index_of(PEEK(0, 1));
   if (rate == 0)
@@ -461,9 +467,9 @@
 
 BEGIN_OPERATOR(delay)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "rate");
+  PORT(0, 1, IN, "mod");
+  PORT(1, 0, OUT, "output");
   Usz rate = index_of(PEEK(0, -1));
   Usz mod_num = index_of(PEEK(0, 1));
   if (rate == 0)
@@ -476,9 +482,9 @@
 
 BEGIN_OPERATOR(if)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "a");
+  PORT(0, 1, IN, "b");
+  PORT(1, 0, OUT, "output");
   Glyph g0 = PEEK(0, -1);
   Glyph g1 = PEEK(0, 1);
   POKE(1, 0, g0 == g1 ? '*' : '.');
@@ -489,12 +495,15 @@
   Isz out_x = (Isz)index_of(PEEK(0, -3));
   Isz out_y = (Isz)index_of(PEEK(0, -2)) + 1;
   Isz len = (Isz)index_of(PEEK(0, -1));
-  PORT(0, -3, IN | PARAM); // x
-  PORT(0, -2, IN | PARAM); // y
-  PORT(0, -1, IN | PARAM); // len
+  PORT(0, -3, IN | PARAM, "x");
+  PORT(0, -2, IN | PARAM, "y");
+  PORT(0, -1, IN | PARAM, "len");
   for (Isz i = 0; i < len; ++i) {
-    PORT(0, i + 1, IN);
-    PORT(out_y, out_x + i, OUT | NONLOCKING);
+    char t[6];
+    sprintf(t, "in%ld", i);
+    PORT(0, i + 1, IN, t);
+    sprintf(t, "out%ld", i);
+    PORT(out_y, out_x + i, OUT | NONLOCKING, t);
     Glyph g = PEEK(0, i + 1);
     POKE_STUNNED(out_y, out_x + i, g);
   }
@@ -502,14 +511,14 @@
 
 BEGIN_OPERATOR(halt)
   LOWERCASE_REQUIRES_BANG;
-  PORT(1, 0, OUT);
+  PORT(1, 0, OUT, "output");
 END_OPERATOR
 
 BEGIN_OPERATOR(increment)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, IN | OUT);
+  PORT(0, -1, IN | PARAM, "step");
+  PORT(0, 1, IN, "mod");
+  PORT(1, 0, IN | OUT, "output");
   Glyph ga = PEEK(0, -1);
   Glyph gb = PEEK(0, 1);
   Usz rate = 1;
@@ -526,8 +535,8 @@
 
 BEGIN_OPERATOR(jump)
   LOWERCASE_REQUIRES_BANG;
-  PORT(-1, 0, IN);
-  PORT(1, 0, OUT);
+  PORT(-1, 0, IN, "val");
+  PORT(1, 0, OUT, "output");
   POKE(1, 0, PEEK(-1, 0));
 END_OPERATOR
 
@@ -538,15 +547,18 @@
   Isz len = (Isz)index_of(PEEK(0, -1));
   if (len == 0)
     len = 1;
-  PORT(0, -1, IN | PARAM);
+  PORT(0, -1, IN | PARAM, "len");
   for (Isz i = 0; i < len; ++i) {
-    PORT(0, i + 1, IN);
+    char t[6];
+    sprintf(t, "in%ld", i);
+    PORT(0, i + 1, IN, t);
     Glyph var = PEEK(0, i + 1);
     if (var != '.') {
       Usz var_idx = index_of(var);
       if (var_idx != 0) {
         Glyph result = extra_params->vars_slots[var_idx];
-        PORT(1, i + 1, OUT);
+        sprintf(t, "out%ld", i);
+        PORT(1, i + 1, OUT, t);
         POKE(1, i + 1, result);
       }
     }
@@ -555,9 +567,9 @@
 
 BEGIN_OPERATOR(lesser)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "a");
+  PORT(0, 1, IN, "b");
+  PORT(1, 0, OUT, "output");
   Glyph ga = PEEK(0, -1);
   Glyph gb = PEEK(0, 1);
   if (ga == '.' || gb == '.') {
@@ -572,9 +584,9 @@
 
 BEGIN_OPERATOR(multiply)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "a");
+  PORT(0, 1, IN, "b");
+  PORT(1, 0, OUT, "output");
   Glyph a = PEEK(0, -1);
   Glyph b = PEEK(0, 1);
   Glyph g = glyph_table[(index_of(a) * index_of(b)) % Glyphs_index_count];
@@ -585,10 +597,10 @@
   LOWERCASE_REQUIRES_BANG;
   Isz in_x = (Isz)index_of(PEEK(0, -2)) + 1;
   Isz in_y = (Isz)index_of(PEEK(0, -1));
-  PORT(0, -1, IN | PARAM);
-  PORT(0, -2, IN | PARAM);
-  PORT(in_y, in_x, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "y");
+  PORT(0, -2, IN | PARAM, "x");
+  PORT(in_y, in_x, IN, "read");
+  PORT(1, 0, OUT, "output");
   POKE(1, 0, PEEK(in_y, in_x));
 END_OPERATOR
 
@@ -596,9 +608,9 @@
   LOWERCASE_REQUIRES_BANG;
   Usz key = index_of(PEEK(0, -2));
   Usz len = index_of(PEEK(0, -1));
-  PORT(0, -1, IN | PARAM);
-  PORT(0, -2, IN | PARAM);
-  PORT(0, 1, IN);
+  PORT(0, -1, IN | PARAM, "len");
+  PORT(0, -2, IN | PARAM, "key");
+  PORT(0, 1, IN, "val");
   if (len == 0)
     return;
   Isz out_x = (Isz)(key % len);
@@ -605,7 +617,7 @@
   for (Usz i = 0; i < len; ++i) {
     LOCK(1, (Isz)i);
   }
-  PORT(1, out_x, OUT);
+  PORT(1, out_x, OUT, "output");
   POKE(1, out_x, PEEK(0, 1));
 END_OPERATOR
 
@@ -615,13 +627,16 @@
   Isz in_y = (Isz)index_of(PEEK(0, -2));
   Isz len = (Isz)index_of(PEEK(0, -1));
   Isz out_x = 1 - len;
-  PORT(0, -3, IN | PARAM); // x
-  PORT(0, -2, IN | PARAM); // y
-  PORT(0, -1, IN | PARAM); // len
+  PORT(0, -3, IN | PARAM, "x");
+  PORT(0, -2, IN | PARAM, "y");
+  PORT(0, -1, IN | PARAM, "len");
   // todo direct buffer manip
   for (Isz i = 0; i < len; ++i) {
-    PORT(in_y, in_x + i, IN);
-    PORT(1, out_x + i, OUT);
+    char t[6];
+    sprintf(t, "in%ld", i);
+    PORT(in_y, in_x + i, IN, t);
+    sprintf(t, "out%ld", i);
+    PORT(1, out_x + i, OUT, t);
     Glyph g = PEEK(in_y, in_x + i);
     POKE(1, out_x + i, g);
   }
@@ -629,9 +644,9 @@
 
 BEGIN_OPERATOR(random)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "min");
+  PORT(0, 1, IN, "max");
+  PORT(1, 0, OUT, "val");
   Usz a = index_of(PEEK(0, -1));
   Usz b = index_of(PEEK(0, 1));
   if (b == 0)
@@ -665,8 +680,8 @@
   LOWERCASE_REQUIRES_BANG;
   Usz key = index_of(PEEK(0, -2));
   Usz len = index_of(PEEK(0, -1));
-  PORT(0, -2, IN | PARAM);
-  PORT(0, -1, IN | PARAM);
+  PORT(0, -2, IN | PARAM, "key");
+  PORT(0, -1, IN | PARAM, "len");
   if (len == 0)
     return;
   Isz read_val_x = (Isz)(key % len) + 1;
@@ -673,8 +688,8 @@
   for (Usz i = 0; i < len; ++i) {
     LOCK(0, (Isz)(i + 1));
   }
-  PORT(0, (Isz)read_val_x, IN);
-  PORT(1, 0, OUT);
+  PORT(0, (Isz)read_val_x, IN, "val");
+  PORT(1, 0, OUT, "output");
   POKE(1, 0, PEEK(0, read_val_x));
 END_OPERATOR
 
@@ -682,9 +697,9 @@
 // simplest-euclidean-rhythm-algorithm-explained/
 BEGIN_OPERATOR(uclid)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, OUT);
+  PORT(0, -1, IN | PARAM, "write");
+  PORT(0, 1, IN, "read");
+  PORT(1, 0, OUT, "output");
   Glyph left = PEEK(0, -1);
   Usz steps = 1;
   if (left != '.' && left != '*')
@@ -699,8 +714,8 @@
 
 BEGIN_OPERATOR(variable)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
+  PORT(0, -1, IN | PARAM, "write");
+  PORT(0, 1, IN, "read");
   Glyph left = PEEK(0, -1);
   Glyph right = PEEK(0, 1);
   if (left != '.') {
@@ -709,7 +724,7 @@
     extra_params->vars_slots[var_idx] = right;
   } else if (right != '.') {
     // Read
-    PORT(1, 0, OUT);
+    PORT(1, 0, OUT, "output");
     Usz var_idx = index_of(right);
     Glyph result = extra_params->vars_slots[var_idx];
     POKE(1, 0, result);
@@ -720,25 +735,25 @@
   LOWERCASE_REQUIRES_BANG;
   Isz out_x = (Isz)index_of(PEEK(0, -2));
   Isz out_y = (Isz)index_of(PEEK(0, -1)) + 1;
-  PORT(0, -2, IN | PARAM); // x
-  PORT(0, -1, IN | PARAM); // y
-  PORT(0, 1, IN);
-  PORT(out_y, out_x, OUT | NONLOCKING);
+  PORT(0, -2, IN | PARAM, "x");
+  PORT(0, -1, IN | PARAM, "y");
+  PORT(0, 1, IN, "val");
+  PORT(out_y, out_x, OUT | NONLOCKING, "output");
   POKE_STUNNED(out_y, out_x, PEEK(0, 1));
 END_OPERATOR
 
 BEGIN_OPERATOR(yump)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN);
-  PORT(0, 1, OUT);
+  PORT(0, -1, IN, "val");
+  PORT(0, 1, OUT, "output");
   POKE(0, 1, PEEK(0, -1));
 END_OPERATOR
 
 BEGIN_OPERATOR(lerp)
   LOWERCASE_REQUIRES_BANG;
-  PORT(0, -1, IN | PARAM);
-  PORT(0, 1, IN);
-  PORT(1, 0, IN | OUT);
+  PORT(0, -1, IN | PARAM, "rate");
+  PORT(0, 1, IN, "target");
+  PORT(1, 0, IN | OUT, "output");
   Glyph g = PEEK(0, -1);
   Isz rate = g == '.' || g == '*' ? 1 : (Isz)index_of(g);
   Isz goal = (Isz)index_of(PEEK(0, 1));
@@ -749,8 +764,9 @@
 
 //////// Run simulation
 
-void orca_run(Glyph *restrict gbuf, Mark *restrict mbuf, Usz height, Usz width,
-              Usz tick_number, Oevent_list *oevent_list, Usz random_seed) {
+void orca_run(Glyph *restrict gbuf, Mark *restrict mbuf, Usz height,
+              Usz width, Usz tick_number, Oevent_list *oevent_list,
+              Usz random_seed, Help *help) {
   Glyph vars_slots[Glyphs_index_count];
   memset(vars_slots, '.', sizeof(vars_slots));
   Oper_extra_params extras;
@@ -758,6 +774,10 @@
   extras.oevent_list = oevent_list;
   extras.random_seed = random_seed;
 
+  const char *name = "empty", *description = "";
+  strcpy(help->name, name);
+  strcpy(help->description, description);
+
   for (Usz iy = 0; iy < height; ++iy) {
     Glyph const *glyph_row = gbuf + iy * width;
     Mark const *mark_row = mbuf + iy * width;
@@ -769,22 +789,30 @@
       if (cell_flags & (Mark_flag_lock | Mark_flag_sleep))
         continue;
       switch (glyph_char) {
-#define UNIQUE_CASE(_oper_char, _oper_name)                                    \
+#define UNIQUE_CASE(_oper_char, _oper_name, _hr_name, _hr_descr)               \
   case _oper_char:                                                             \
+    name = _hr_name;                                                           \
+    description = _hr_descr;                                                   \
     oper_behavior_##_oper_name(gbuf, mbuf, height, width, iy, ix, tick_number, \
-                               &extras, cell_flags, glyph_char);               \
+                               &extras, cell_flags, glyph_char, help);         \
     break;
 
-#define ALPHA_CASE(_upper_oper_char, _oper_name)                               \
+#define ALPHA_CASE(_upper_oper_char, _oper_name, _hr_name, _hr_descr)          \
   case _upper_oper_char:                                                       \
   case (char)(_upper_oper_char | 1 << 5):                                      \
+    name = _hr_name;                                                           \
+    description = _hr_descr;                                                   \
     oper_behavior_##_oper_name(gbuf, mbuf, height, width, iy, ix, tick_number, \
-                               &extras, cell_flags, glyph_char);               \
+                               &extras, cell_flags, glyph_char, help);         \
     break;
         UNIQUE_OPERATORS(UNIQUE_CASE)
         ALPHA_OPERATORS(ALPHA_CASE)
 #undef UNIQUE_CASE
 #undef ALPHA_CASE
+      }
+      if (ix == help->x && iy == help->y) {
+        strcpy(help->name, name);
+        strcpy(help->description, description);
       }
     }
   }
--- a/sim.h
+++ b/sim.h
@@ -2,6 +2,13 @@
 #include "base.h"
 #include "vmio.h"
 
+typedef struct {
+  char name[16];
+  char description[32];
+  int  x;
+  int  y;
+}Help;
+
 void orca_run(Glyph *restrict gbuffer, Mark *restrict mbuffer, Usz height,
               Usz width, Usz tick_number, Oevent_list *oevent_list,
-              Usz random_seed);
+              Usz random_seed, Help *help);
--- a/tui_main.c
+++ b/tui_main.c
@@ -1269,9 +1269,10 @@
 staticni void clear_and_run_vm(Glyph *restrict gbuf, Mark *restrict mbuf,
                                Usz height, Usz width, Usz tick_number,
                                Oevent_list *oevent_list, Usz random_seed) {
+  Help help;
   mbuffer_clear(mbuf, height, width);
   oevent_list_clear(oevent_list);
-  orca_run(gbuf, mbuf, height, width, tick_number, oevent_list, random_seed);
+  orca_run(gbuf, mbuf, height, width, tick_number, oevent_list, random_seed, &help);
 }
 
 staticni void ged_do_stuff(Ged *a) {