|
|
@@ -8,11 +8,13 @@
|
|
|
* Linked with every V3 program image.
|
|
|
*/
|
|
|
|
|
|
+#define _GNU_SOURCE
|
|
|
#include <unistd.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
|
#include <string.h>
|
|
|
#include <time.h>
|
|
|
+#include <ucontext.h>
|
|
|
|
|
|
/* Write the contents of a string descriptor to fd 1. Returns the
|
|
|
number of bytes written. */
|
|
|
@@ -496,3 +498,335 @@ double m2readreal(void)
|
|
|
if (scanf("%lf", &v) != 1) v = 0.0;
|
|
|
return v;
|
|
|
}
|
|
|
+
|
|
|
+/* ---------------- PIM coroutines (SYSTEM.NEWPROCESS/TRANSFER/...) --------
|
|
|
+ *
|
|
|
+ * A PROCESS is a pointer to a ucontext_t. NEWPROCESS builds a context
|
|
|
+ * over the caller-supplied workspace; TRANSFER saves the running
|
|
|
+ * context into *a and resumes *b. Coroutine bodies are compiled with a
|
|
|
+ * leading static-link argument; module-level bodies (the usual case for
|
|
|
+ * NEWPROCESS) take 0. */
|
|
|
+
|
|
|
+void m2_newprocess(void *p, void *workspace, long size, void **c)
|
|
|
+{
|
|
|
+ ucontext_t *uc;
|
|
|
+ if (c == NULL) return;
|
|
|
+ uc = (ucontext_t *)malloc(sizeof(ucontext_t));
|
|
|
+ if (uc == NULL) { *c = NULL; return; }
|
|
|
+ if (getcontext(uc) != 0) { free(uc); *c = NULL; return; }
|
|
|
+ uc->uc_stack.ss_sp = workspace;
|
|
|
+ uc->uc_stack.ss_size = (size_t)size;
|
|
|
+ uc->uc_link = NULL;
|
|
|
+ makecontext(uc, (void (*)(void))p, 1, (int)0);
|
|
|
+ *c = uc;
|
|
|
+}
|
|
|
+
|
|
|
+void m2_transfer(void **a, void **b)
|
|
|
+{
|
|
|
+ ucontext_t *save;
|
|
|
+ ucontext_t *go;
|
|
|
+ if ((a == NULL) || (b == NULL)) return;
|
|
|
+ save = (ucontext_t *)*a;
|
|
|
+ go = (ucontext_t *)*b;
|
|
|
+ if (go == NULL) return;
|
|
|
+ if (save == NULL) {
|
|
|
+ save = (ucontext_t *)malloc(sizeof(ucontext_t));
|
|
|
+ if (save == NULL) return;
|
|
|
+ *a = save;
|
|
|
+ }
|
|
|
+ swapcontext(save, go);
|
|
|
+}
|
|
|
+
|
|
|
+/* IOTRANSFER: without a device-interrupt subsystem this degrades to a
|
|
|
+ plain transfer to b (classic PIM resumes a when the interrupt
|
|
|
+ fires). The interrupt number is accepted and ignored. */
|
|
|
+void m2_iotransfer(void **a, void **b, long interruptNo)
|
|
|
+{
|
|
|
+ (void)interruptNo;
|
|
|
+ m2_transfer(a, b);
|
|
|
+}
|
|
|
+
|
|
|
+/* ---------------- text-IO library support ---------------- */
|
|
|
+
|
|
|
+/* Read one line from stdin (newline dropped) into the string
|
|
|
+ descriptor's data area; NUL-terminated. Returns the length. */
|
|
|
+long m2readline(long *desc, int max)
|
|
|
+{
|
|
|
+ char *dst = (char *)(desc + 1);
|
|
|
+ long n = 0;
|
|
|
+ int c;
|
|
|
+ if (max < 0) max = 0;
|
|
|
+ while (n < max) {
|
|
|
+ c = getchar();
|
|
|
+ if (c == EOF) break;
|
|
|
+ if (c == '\n') break;
|
|
|
+ if (c == '\r') continue;
|
|
|
+ dst[n++] = (char)c;
|
|
|
+ }
|
|
|
+ dst[n] = 0;
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+/* Write a signed integer right-justified in a field of width `wid`
|
|
|
+ to fd 1 (wid = 0 => exactly one leading space). */
|
|
|
+long m2writeintwidth(long v, int wid)
|
|
|
+{
|
|
|
+ char buf[64];
|
|
|
+ int n;
|
|
|
+ if (wid == 0)
|
|
|
+ n = snprintf(buf, sizeof buf, " %ld", v);
|
|
|
+ else
|
|
|
+ n = snprintf(buf, sizeof buf, "%*ld", wid, v);
|
|
|
+ if (n > 0) write(1, buf, (size_t)n);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Format a REAL into a descriptor: mode 0 = fixed (prec = decimal
|
|
|
+ places), 1 = floating (prec = significant figures), 2 = engineering
|
|
|
+ (prec = significant figures, exponent a multiple of three).
|
|
|
+ desc[0] is the capacity; on return it holds the length. */
|
|
|
+static long m2putstr(char *dst, long cap, const char *src)
|
|
|
+{
|
|
|
+ long n = (long)strlen(src);
|
|
|
+ if (cap <= 0) return 0;
|
|
|
+ if (n > cap - 1) n = cap - 1;
|
|
|
+ memcpy(dst, src, (size_t)n);
|
|
|
+ dst[n] = 0;
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+long m2realconv(double x, int mode, int prec, long *desc)
|
|
|
+{
|
|
|
+ char buf[256];
|
|
|
+ char *dst = (char *)(desc + 1);
|
|
|
+ long cap = desc[0];
|
|
|
+ int n = 0;
|
|
|
+ if (prec < 0) prec = 0;
|
|
|
+ if (prec > 40) prec = 40;
|
|
|
+ if (mode == 0) {
|
|
|
+ n = snprintf(buf, sizeof buf, "%.*f", prec, x);
|
|
|
+ } else if (mode == 1) {
|
|
|
+ n = snprintf(buf, sizeof buf, "%.*E",
|
|
|
+ prec > 0 ? prec - 1 : 0, x);
|
|
|
+ } else {
|
|
|
+ /* engineering: mantissa in [1,1000), exponent multiple of 3 */
|
|
|
+ int e;
|
|
|
+ double m = x;
|
|
|
+ if (m != 0.0) {
|
|
|
+ e = 0;
|
|
|
+ while (m >= 1000.0) { m /= 1000.0; e += 3; }
|
|
|
+ while (m < 1.0) { m *= 1000.0; e -= 3; }
|
|
|
+ } else {
|
|
|
+ e = 0;
|
|
|
+ }
|
|
|
+ n = snprintf(buf, sizeof buf, "%.*fE%+d",
|
|
|
+ prec > 0 ? prec - 1 : 0, m, e);
|
|
|
+ }
|
|
|
+ if (n < 0) n = 0;
|
|
|
+ return m2putstr(dst, cap, buf);
|
|
|
+}
|
|
|
+
|
|
|
+/* Termination flags: HALT aborts the image, so neither is ever
|
|
|
+ observably TRUE; provided so TERMINATION links. */
|
|
|
+long m2terminating(void) { return 0; }
|
|
|
+long m2hashalted(void) { return 0; }
|
|
|
+
|
|
|
+/* ---------------- DynamicStrings (heap C strings) ----------------
|
|
|
+ *
|
|
|
+ * A DynamicStrings.String is a malloc'd NUL-terminated char buffer.
|
|
|
+ * Procedures that return a String return the buffer address; those
|
|
|
+ * that take a String receive it in the first `long` argument. */
|
|
|
+
|
|
|
+static char *m2dsdup(const char *s)
|
|
|
+{
|
|
|
+ size_t n;
|
|
|
+ char *p;
|
|
|
+ if (s == NULL) s = "";
|
|
|
+ n = strlen(s);
|
|
|
+ p = (char *)malloc(n + 1);
|
|
|
+ if (p == NULL) return NULL;
|
|
|
+ memcpy(p, s, n + 1);
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsinit(long *a) { return (long)m2dsdup((const char *)(a + 1)); }
|
|
|
+long m2dskill(char *s) { free(s); return 0; }
|
|
|
+long m2dslength(const char *s) { return s ? (long)strlen(s) : 0; }
|
|
|
+long m2dsdupstr(const char *s) { return (long)m2dsdup(s); }
|
|
|
+
|
|
|
+long m2dsconcat(char *a, const char *b)
|
|
|
+{
|
|
|
+ size_t na, nb;
|
|
|
+ char *p;
|
|
|
+ if (b == NULL) b = "";
|
|
|
+ na = a ? strlen(a) : 0;
|
|
|
+ nb = strlen(b);
|
|
|
+ p = (char *)realloc(a, na + nb + 1);
|
|
|
+ if (p == NULL) return (long)a;
|
|
|
+ memcpy(p + na, b, nb + 1);
|
|
|
+ return (long)p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsconcatchar(char *a, int ch)
|
|
|
+{
|
|
|
+ size_t na = a ? strlen(a) : 0;
|
|
|
+ char *p = (char *)realloc(a, na + 2);
|
|
|
+ if (p == NULL) return (long)a;
|
|
|
+ p[na] = (char)ch;
|
|
|
+ p[na + 1] = 0;
|
|
|
+ return (long)p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsassign(char *a, const char *b)
|
|
|
+{
|
|
|
+ size_t nb;
|
|
|
+ char *p;
|
|
|
+ if (b == NULL) b = "";
|
|
|
+ nb = strlen(b);
|
|
|
+ p = (char *)realloc(a, nb + 1);
|
|
|
+ if (p == NULL) return (long)a;
|
|
|
+ memcpy(p, b, nb + 1);
|
|
|
+ return (long)p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dseq(const char *a, const char *b)
|
|
|
+{
|
|
|
+ if (a == NULL) a = "";
|
|
|
+ if (b == NULL) b = "";
|
|
|
+ return strcmp(a, b) == 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* EqualArray: the second operand arrives as the *descriptor* of a V3
|
|
|
+ CHAR-array actual (V3 passes array actuals as their descriptor). */
|
|
|
+long m2dseqarr(const char *s, long *a)
|
|
|
+{
|
|
|
+ return m2dseq(s, (const char *)(a + 1));
|
|
|
+}
|
|
|
+
|
|
|
+long m2dschar(const char *s, int i)
|
|
|
+{
|
|
|
+ int n;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ n = (int)strlen(s);
|
|
|
+ if (i < 0) i = n + i;
|
|
|
+ if (i < 0 || i >= n) return 0;
|
|
|
+ return (unsigned char)s[i];
|
|
|
+}
|
|
|
+
|
|
|
+long m2dscopyout(long *dst, const char *s)
|
|
|
+{
|
|
|
+ char *d = (char *)(dst + 1);
|
|
|
+ long cap = dst[0];
|
|
|
+ size_t n;
|
|
|
+ if (s == NULL) s = "";
|
|
|
+ n = strlen(s);
|
|
|
+ if (cap < 0) cap = 0;
|
|
|
+ if (n > (size_t)cap) n = (size_t)cap;
|
|
|
+ memcpy(d, s, n);
|
|
|
+ d[n] = 0;
|
|
|
+ return (long)n;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsslice(const char *s, int low, int high)
|
|
|
+{
|
|
|
+ int n, lo, hi;
|
|
|
+ char *p;
|
|
|
+ if (s == NULL) s = "";
|
|
|
+ n = (int)strlen(s);
|
|
|
+ lo = low;
|
|
|
+ hi = high;
|
|
|
+ if (lo < 0) lo = n + lo;
|
|
|
+ if (lo < 0) lo = 0;
|
|
|
+ if (hi == 0) hi = n;
|
|
|
+ else if (hi < 0) hi = n + hi;
|
|
|
+ if (hi > n) hi = n;
|
|
|
+ if (hi < lo) hi = lo;
|
|
|
+ p = (char *)malloc((size_t)(hi - lo) + 1);
|
|
|
+ if (p == NULL) return 0;
|
|
|
+ memcpy(p, s + lo, (size_t)(hi - lo));
|
|
|
+ p[hi - lo] = 0;
|
|
|
+ return (long)p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsindex(const char *s, int ch, int o)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ if (s == NULL) return -1;
|
|
|
+ for (i = o; s[i] != 0; i++)
|
|
|
+ if ((unsigned char)s[i] == (unsigned char)ch) return i;
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsrindex(const char *s, int ch, int o)
|
|
|
+{
|
|
|
+ int n, i;
|
|
|
+ if (s == NULL) return -1;
|
|
|
+ n = (int)strlen(s);
|
|
|
+ if (o >= n) o = n - 1;
|
|
|
+ for (i = o; i >= 0; i--)
|
|
|
+ if ((unsigned char)s[i] == (unsigned char)ch) return i;
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsmult(const char *s, int n)
|
|
|
+{
|
|
|
+ size_t len, i;
|
|
|
+ char *p;
|
|
|
+ if (s == NULL) s = "";
|
|
|
+ if (n <= 0) return (long)m2dsdup("");
|
|
|
+ len = strlen(s);
|
|
|
+ p = (char *)malloc(len * (size_t)n + 1);
|
|
|
+ if (p == NULL) return 0;
|
|
|
+ for (i = 0; i < (size_t)n; i++) memcpy(p + i * len, s, len);
|
|
|
+ p[len * (size_t)n] = 0;
|
|
|
+ return (long)p;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsreplacechar(char *s, int from, int to)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ for (i = 0; s[i] != 0; i++)
|
|
|
+ if ((unsigned char)s[i] == (unsigned char)from) s[i] = (char)to;
|
|
|
+ return (long)s;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dsupper(char *s)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ for (i = 0; s[i] != 0; i++)
|
|
|
+ if (s[i] >= 'a' && s[i] <= 'z') s[i] = (char)(s[i] - 32);
|
|
|
+ return (long)s;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dslower(char *s)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ for (i = 0; s[i] != 0; i++)
|
|
|
+ if (s[i] >= 'A' && s[i] <= 'Z') s[i] = (char)(s[i] + 32);
|
|
|
+ return (long)s;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dstrimprefix(char *s)
|
|
|
+{
|
|
|
+ char *p;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ p = s;
|
|
|
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
|
|
|
+ if (p != s) memmove(s, p, strlen(p) + 1);
|
|
|
+ return (long)s;
|
|
|
+}
|
|
|
+
|
|
|
+long m2dstrimpostfix(char *s)
|
|
|
+{
|
|
|
+ size_t n;
|
|
|
+ if (s == NULL) return 0;
|
|
|
+ n = strlen(s);
|
|
|
+ while (n > 0 && (s[n-1] == ' ' || s[n-1] == '\t' ||
|
|
|
+ s[n-1] == '\n' || s[n-1] == '\r')) {
|
|
|
+ s[--n] = 0;
|
|
|
+ }
|
|
|
+ return (long)s;
|
|
|
+}
|