shithub: sl

Download patch

ref: ddd49d6806fbc3f2c9128c38d6b703e9cd6b7225
parent: 17c3518fa9ddc3695dd21e35e360ba78a289374a
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Jan 7 21:07:02 EST 2025

crude initial MacOS 9.x (PowerPC) port

--- a/3rd/fn.c
+++ b/3rd/fn.c
@@ -118,7 +118,7 @@
 Tbl *
 Tsetl(Tbl *tbl, const char *key, size_t len, void *val)
 {
-	assert(!Tindex_branch((Tindex)val) && len <= Tmaxlen);
+	assert(!Tindex_branch((Tindex)(uintptr_t)val) && len <= Tmaxlen);
 	if(val == nil)
 		return Tdell(tbl, key, len);
 	// First leaf in an empty tbl?
--- a/3rd/fn.h
+++ b/3rd/fn.h
@@ -49,16 +49,16 @@
 	} \
 	struct dummy
 
-Tset_field((void *),           ptr,   Trie *,       twigs);
-Tset_field((Tindex),           index, Tindex,       index);
-Tset_field((void *)(uint64_t), ptr,   const char *, key);
-Tset_field((Tindex),           index, void *,       val);
+Tset_field((void *),            ptr,   Trie *,       twigs);
+Tset_field((Tindex),            index, Tindex,       index);
+Tset_field((void *)(uintptr_t), ptr,   const char *, key);
+Tset_field((Tindex)(uintptr_t), index, void *,       val);
 
 static inline bool Tindex_branch(Tindex i);
 
 static inline bool isbranch(Trie *t)
 {
-	return(Tindex_branch(t->index));
+	return Tindex_branch(t->index);
 }
 
 #define Tbranch(t) assert(isbranch(t))
@@ -75,7 +75,7 @@
 
 Tcheck_get(Trie *,       Tbranch, twigs, t->ptr);
 Tcheck_get(const char *, Tleaf,   key,   t->ptr);
-Tcheck_get(void *,       Tleaf,   val,   (void*)t->index);
+Tcheck_get(void *,       Tleaf,   val,   (void*)(uintptr_t)t->index);
 
 // index word layout
 
--- /dev/null
+++ b/3rd/mp/mp.h
@@ -1,0 +1,178 @@
+#pragma once
+
+#ifdef BITS64
+typedef uint64_t mpdigit;
+#else
+typedef uint32_t mpdigit;
+#endif
+
+typedef union FPdbleword FPdbleword;
+union FPdbleword
+{
+	double	x;
+	struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+		uint32_t lo;
+		uint32_t hi;
+#else
+		uint32_t hi;
+		uint32_t lo;
+#endif
+	};
+};
+
+#define mpdighi  (((mpdigit)1)<<(Dbits-1))
+#define DIGITS(x) ((int)(x) >= -(Dbits-1) ? ((Dbits - 1 + (x))/Dbits) : 0)
+
+extern	int	dec16(uint8_t*, int, char*, int);
+extern	int	enc16(char*, int, uint8_t*, int);
+extern	mpdigit	dec16chr(int);
+extern	int	enc16chr(int);
+
+/*
+ * the code assumes mpdigit to be at least an int
+ * mpdigit must be an atomic type.  mpdigit is defined
+ * in the architecture specific u.h
+ */
+typedef struct mpint mpint;
+
+struct mpint
+{
+	mpdigit	*p;
+	uint32_t	size;	/* allocated digits */
+	uint32_t	top;	/* significant digits */
+	int	sign;	/* +1 or -1 */
+	int	flags;
+};
+
+enum
+{
+	MPstatic=	0x01,	/* static constant */
+	MPnorm=		0x02,	/* normalization status */
+	MPtimesafe=	0x04,	/* request time invariant computation */
+
+	Dbytes=		sizeof(mpdigit),	/* bytes per digit */
+	Dbits=		Dbytes*8		/* bits per digit */
+};
+
+/* allocation */
+void	mpsetminbits(int n);	/* newly created mpint's get at least n bits */
+mpint*	mpnew(int n);		/* create a new mpint with at least n bits */
+void	mpfree(mpint *b);
+void	mpbits(mpint *b, int n);	/* ensure that b has at least n bits */
+mpint*	mpnorm(mpint *b);		/* dump leading zeros */
+mpint*	mpcopy(mpint *b);
+void	mpassign(mpint *old, mpint *new);
+
+/* random bits */
+mpint*	mprand(int bits, void (*gen)(uint8_t*, int), mpint *b);
+/* return uniform random [0..n-1] */
+mpint*	mpnrand(mpint *n, void (*gen)(uint8_t*, int), mpint *b);
+
+/* conversion */
+mpint*	strtomp(const char*, char**, int, mpint*);	/* ascii */
+char*	mptoa(mpint*, int, char*, int);
+mpint*	letomp(uint8_t*, uint32_t, mpint*);	/* byte array, little-endian */
+int	mptole(mpint*, uint8_t*, uint32_t, uint8_t**);
+void	mptolel(mpint *b, uint8_t *p, int n);
+mpint*	betomp(uint8_t*, uint32_t, mpint*);	/* byte array, big-endian */
+int	mptobe(mpint*, uint8_t*, uint32_t, uint8_t**);
+void	mptober(mpint *b, uint8_t *p, int n);
+uint32_t	mptoui(mpint*);			/* unsigned int */
+mpint*	uitomp(uint32_t, mpint*);
+int	mptoi(mpint*);			/* int */
+mpint*	itomp(int, mpint*);
+uint64_t	mptouv(mpint*);			/* unsigned int64_t */
+mpint*	uvtomp(uint64_t, mpint*);
+int64_t	mptov(mpint*);			/* int64_t */
+mpint*	vtomp(int64_t, mpint*);
+double	mptod(mpint*);			/* double */
+mpint*	dtomp(double, mpint*);
+
+/* divide the 2 digit dividend by the one digit divisor and stick in quotient */
+/* we assume that the result is one digit - overflow is all 1's */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* in the following, the result mpint may be */
+/* the same as one of the inputs. */
+void	mpadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpsub(mpint *b1, mpint *b2, mpint *diff);	/* diff = b1-b2 */
+void	mpleft(mpint *b, int shift, mpint *res);	/* res = b<<shift */
+void	mpright(mpint *b, int shift, mpint *res);	/* res = b>>shift */
+void	mpmul(mpint *b1, mpint *b2, mpint *prod);	/* prod = b1*b2 */
+void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	/* res = b**e mod m */
+void	mpmod(mpint *b, mpint *m, mpint *remainder);	/* remainder = b mod m */
+
+/* logical operations */
+void	mpand(mpint *b1, mpint *b2, mpint *res);
+void	mpbic(mpint *b1, mpint *b2, mpint *res);
+void	mpor(mpint *b1, mpint *b2, mpint *res);
+void	mpnot(mpint *b, mpint *res);
+void	mpxor(mpint *b1, mpint *b2, mpint *res);
+void	mpasr(mpint *b, int shift, mpint *res);
+
+/* modular arithmetic, time invariant when 0≤b1≤m-1 and 0≤b2≤m-1 */
+void	mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum);	/* sum = b1+b2 % m */
+void	mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff);	/* diff = b1-b2 % m */
+void	mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod);	/* prod = b1*b2 % m */
+
+/* quotient = dividend/divisor, remainder = dividend % divisor */
+void	mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
+
+/* return neg, 0, pos as b1-b2 is neg, 0, pos */
+int	mpcmp(mpint *b1, mpint *b2);
+
+/* res = s != 0 ? b1 : b2 */
+void	mpsel(int s, mpint *b1, mpint *b2, mpint *res);
+
+/* extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d */
+void	mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
+
+/* res = b**-1 mod m */
+void	mpinvert(mpint *b, mpint *m, mpint *res);
+
+/* bit counting */
+uint32_t	mpsignif(mpint*);	/* number of sigificant bits in mantissa */
+uint32_t	mplowbits0(mpint*);	/* k, where n = 2**k * q for odd q */
+
+/* well known constants */
+extern mpint	*mpzero, *mpone, *mptwo;
+
+/* sum[0:alen] = a[0:alen-1] + b[0:blen-1] */
+/* prereq: alen >= blen, sum has room for alen+1 digits */
+void	mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
+
+/* diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] */
+/* prereq: alen >= blen, diff has room for alen digits */
+void	mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
+
+/* p[0:n] += m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+void	mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:n] -= m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+int	mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:alen+blen-1] = a[0:alen-1] * b[0:blen-1] */
+/* prereq: alen >= blen, p has room for m*n digits */
+void	mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+void	mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+
+/* sign of a - b or zero if the same */
+int	mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
+int	mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen);
+
+/* playing with magnitudes */
+int	mpmagcmp(mpint *b1, mpint *b2);
+void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+
+/* fast field arithmetic */
+typedef struct Mfield	Mfield;
+
+struct Mfield
+{
+	mpint m;
+	int	(*reduce)(Mfield*, mpint*, mpint*);
+};
--- a/3rd/mp/mpadd.c
+++ b/3rd/mp/mpadd.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // sum = abs(b1) + abs(b2), i.e., add the magnitudes
 void
--- a/3rd/mp/mpaux.c
+++ b/3rd/mp/mpaux.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 static mpdigit _mptwodata[1] = { 2 };
 static mpint _mptwo =
--- a/3rd/mp/mpcmp.c
+++ b/3rd/mp/mpcmp.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // return neg, 0, pos as abs(b1)-abs(b2) is neg, 0, pos
 int
--- a/3rd/mp/mpdigdiv.c
+++ b/3rd/mp/mpdigdiv.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 //
 //	divide two digits by one and return quotient
--- a/3rd/mp/mpdiv.c
+++ b/3rd/mp/mpdiv.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // division ala knuth, seminumerical algorithms, pp 237-238
 // the numbers are stored backwards to what knuth expects so j
--- a/3rd/mp/mpfmt.c
+++ b/3rd/mp/mpfmt.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 static int
 topow2(mpint *b, char *buf, int len, int s)
--- a/3rd/mp/mpleft.c
+++ b/3rd/mp/mpleft.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // res = b << shift
 void
--- a/3rd/mp/mplogic.c
+++ b/3rd/mp/mplogic.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 /*
 	mplogic calculates b1|b2 subject to the
--- a/3rd/mp/mpmul.c
+++ b/3rd/mp/mpmul.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 //
 //  from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
--- a/3rd/mp/mpright.c
+++ b/3rd/mp/mpright.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // res = b >> shift
 void
--- a/3rd/mp/mpsub.c
+++ b/3rd/mp/mpsub.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // diff = abs(b1) - abs(b2), i.e., subtract the magnitudes
 void
--- a/3rd/mp/mptobe.c
+++ b/3rd/mp/mptobe.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // convert an mpint into a big endian byte array (most significant byte first; left adjusted)
 //   return number of bytes converted
--- a/3rd/mp/mptober.c
+++ b/3rd/mp/mptober.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 void
 mptober(mpint *b, uint8_t *p, int n)
--- a/3rd/mp/mptod.c
+++ b/3rd/mp/mptod.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 extern double D_PINF, D_NINF;
 
--- a/3rd/mp/mptoi.c
+++ b/3rd/mp/mptoi.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 /*
  *  this code assumes that mpdigit is at least as
--- a/3rd/mp/mptoui.c
+++ b/3rd/mp/mptoui.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 /*
  *  this code assumes that mpdigit is at least as
--- a/3rd/mp/mptouv.c
+++ b/3rd/mp/mptouv.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 #define VLDIGITS (int)(sizeof(int64_t)/Dbytes)
 
--- a/3rd/mp/mptov.c
+++ b/3rd/mp/mptov.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 #define VLDIGITS (int)(sizeof(int64_t)/Dbytes)
 
--- a/3rd/mp/mpvecadd.c
+++ b/3rd/mp/mpvecadd.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // prereq: alen >= blen, sum has at least blen+1 digits
 void
--- a/3rd/mp/mpveccmp.c
+++ b/3rd/mp/mpveccmp.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 int
 mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen)
--- a/3rd/mp/mpvecdigmuladd.c
+++ b/3rd/mp/mpvecdigmuladd.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 #define LO(x) ((x) & ((((mpdigit)1)<<(Dbits/2))-1))
 #define HI(x) ((x) >> (Dbits/2))
--- a/3rd/mp/mpvecsub.c
+++ b/3rd/mp/mpvecsub.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 // prereq: a >= b, alen >= blen, diff has at least alen digits
 void
--- a/3rd/mp/mpvectscmp.c
+++ b/3rd/mp/mpvectscmp.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 int
 mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen)
--- a/3rd/mp/strtomp.c
+++ b/3rd/mp/strtomp.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 static const char *
 frompow2(const char *a, mpint *b, int s)
--- a/3rd/mp/u16.c
+++ b/3rd/mp/u16.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "mp.h"
 
 #define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
 
--- a/3rd/utf/rune.c
+++ b/3rd/utf/rune.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "utf.h"
 
 enum
 {
--- a/3rd/utf/runeistype.c
+++ b/3rd/utf/runeistype.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "utf.h"
 #include "runeistypedata"
 
 int
--- a/3rd/utf/runetotype.c
+++ b/3rd/utf/runetotype.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "utf.h"
 #include "runetotypedata"
 
 Rune
--- /dev/null
+++ b/3rd/utf/utf.h
@@ -1,0 +1,27 @@
+#pragma once
+
+enum {
+	Runeself = 0x80,
+	Runeerror = 0xfffd,
+	Runemax = 0x10ffff,
+	UTFmax = 4,
+};
+
+typedef uint32_t Rune;
+
+int chartorune(Rune *rune, const char *str);
+int runetochar(char *str, const Rune *rune);
+int runenlen(const Rune *r, int nrune);
+int fullrune(const char *str, int n);
+int runelen(Rune c);
+Rune tolowerrune(Rune c);
+Rune toupperrune(Rune c);
+Rune totitlerune(Rune c);
+int islowerrune(Rune c);
+int isupperrune(Rune c);
+int isalpharune(Rune c);
+int istitlerune(Rune c);
+int isspacerune(Rune c);
+int isdigitrune(Rune c);
+
+int utfnlen(const char *s, long m);
--- a/3rd/utf/utfnlen.c
+++ b/3rd/utf/utfnlen.c
@@ -1,4 +1,5 @@
 #include "platform.h"
+#include "utf.h"
 
 int
 utfnlen(const char *s, long m)
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 This is a reanimation of
 https://github.com/lambdaconservatory/femtolisp with bigger plans.
 
-Supported OS: [9front](http://9front.org), Unix-like operating systems (OpenBSD, NetBSD, Linux, etc).
+Supported OS: [9front](http://9front.org), Unix-like operating systems (OpenBSD, NetBSD, Linux, etc), MacOS 9.x.
 
 Supported CPUs: any decent 32 or 64-bit, little or big endian.
 
@@ -42,6 +42,19 @@
 ### Plan 9
 
 	mk all test
+
+### MacOS 9.x (PowerPC)
+
+Install and build [Retro68](https://github.com/autc04/Retro68).
+
+	ln -s path-to-Retro68-build/toolchain cross/macos-toolchain
+	meson setup build . -Dbuildtype=minsize --cross-file cross/powerpc-apple.txt -Dstrip=true
+	ninja -C build
+
+`build/flisp.bin` can be then copied to your MacOS, it contains the executable.
+
+NOTE: this isn't a full-fledged port and has a rather low priority at the moment. Some things
+are not working.
 
 ## Characteristics
 
--- /dev/null
+++ b/cross/powerpc-apple.txt
@@ -1,0 +1,40 @@
+[constants]
+toolchain = '@DIRNAME@/macos-toolchain/'
+path = toolchain + 'bin/'
+prefix = path + 'powerpc-apple-macos-'
+	add_project_arguments(
+		'-fdata-sections',
+		'-ffunction-sections',
+		'-D__macos__',
+		language: 'c',
+	)
+	add_project_link_arguments(
+		'-Wl,-gc-sections',
+		language: 'cpp',
+	)
+
+[built-in options]
+c_args = ['-fdata-sections', '-ffunction-sections', '-D__macos__']
+cpp_link_args = ['-Wl,-gc-sections']
+
+[properties]
+rincludes = toolchain + 'RIncludes'
+makepef = path + 'MakePEF'
+rez = path + 'Rez'
+
+[binaries]
+c = prefix + 'gcc'
+as = prefix + 'as'
+ar = prefix + 'ar'
+cpp = prefix + 'g++'
+pkg-config = prefix + 'pkg-config'
+strip = prefix + 'strip'
+
+[host_machine]
+system = 'macos'
+cpu_family = 'ppc'
+cpu = 'ppc'
+endian = 'big'
+
+[project options]
+build-tests = 'disabled'
--- a/flisp.boot
+++ b/flisp.boot
@@ -17,7 +17,7 @@
 	      0)
 	    *properties* #table(*funvars* #table(>= ((a . rest))  void? ((x))  length= ((lst n))  help ((term))  lz-unpack ((data
   :to destination)
-  (data :size decompressed-bytes))  = ((a . rest))  <= ((a . rest))  car ((lst))  /= ((a . rest))  void (rest)  *prompt* (nil)  nan? ((x))  lz-pack ((data
+  (data :size decompressed-bytes))  = ((a . rest))  <= ((a . rest))  car ((lst))  /= ((a . rest))  void (rest)  *prompt* (#f)  nan? ((x))  lz-pack ((data
   (level 0)))  cons? ((value))  vm-stats (nil)  * ((number…))  cdr ((lst))  > ((a . rest))  + ((number…)))  *doc* #table(+ "Return sum of the numbers or 0 with no arguments."  >= "Return #t if the arguments are in non-increasing order (previous\none is greater than or equal to the next one)."  void? "Return #t if x is #<void> and #f otherwise."  length= "Bounded length test.\nUse this instead of (= (length lst) n), since it avoids unnecessary\nwork and always terminates."  help "Display documentation for the specified term, if available."  lz-unpack "Return decompressed data previously compressed using lz-pack.\nEither destination for the decompressed data or the expected size of\nthe decompressed data must be specified.  In the latter case a new\narray is allocated."  = "Return #t if the arguments are equal."  <= "Return #t if the arguments are in non-decreasing order (previous\none is less than or equal to the next one)."  *builtins* "VM instructions as closures."  car "Returns the first element of a list or nil if not available."  /= "Return #t if not all arguments are equal. Shorthand for (not (= …))."  void "Return the constant #<void> while ignoring any arguments.\n#<void> is mainly used when a function has side effects but does not\nproduce any meaningful value to return, so even though #t or nil could\nbe returned instead, in case of #<void> alone, REPL will not print\nit."  arg-counts "VM instructions mapped to their expected arguments count."  *prompt* "Function called by REPL to signal the user input is required.\nDefault function prints \"#;> \"."  nan? "Return #t if the argument is NaN, regardless of the sign."  Instructions "VM instructions mapped to their encoded byte representation."  lz-pack "Return data compressed using Lempel-Ziv.\nThe data must be an array, returned value will have the same type.\nThe optional level is between 0 and 10.  With level 0 a simple LZSS\nusing hashing will be performed.  Levels between 1 and 9 offer a\ntrade-off between time/space and ratio.  Level 10 is optimal but very\nslow."  vm-stats "Print various VM-related information, such as the number of GC calls\nso far, heap and stack size, etc."  cons? "Returns #t if the value is a cons cell."  * "Return product of the numbers or 1 with no arguments."  > "Return #t if the arguments are in strictly decreasing order (previous\none is greater than the next one)."  cdr "Returns the tail of a list or nil if not available."  *properties* "All properties of symbols recorded with putprop are recorded in this table."))
 	    *runestring-type* (array rune) *string-type* (array byte)
 	    *syntax-environment* #table(bcode:nconst #fn("7000n1200r2e3:" #(aref))  with-input-from #fn("<000z12021e1220e2e1e12315163:" #(#fn(nconc)
@@ -56,13 +56,13 @@
 	    > #fn(";000z1\x8d\x8a6862086>1_486<^10162:" #(#fn("7000n21V;IE041<0L2;3;04A<1<1=62:" #())) >)
 	    >= #fn(";000z1\x8d\x8a6862086>1_486<^10162:" #(#fn("7000n21V;IL0401<L2;I5040\x8e340O:A<1<1=62:" #())) >=)
 	    Instructions #table(call.l 81  trycatch 75  largc 79  loadg.l 68  aref2 23  box 90  cadr 36  argc 62  setg 71  load0 21  nan? 94  vector? 45  fixnum? 41  loadc0 17  loada0 0  div0 59  keyargs 89  call 5  loada.l 69  brt.l 50  sub2 78  add2 29  loadc.l 70  loadc 9  builtin? 43  set-car! 47  brt 25  ret 10  loadi8 66  tapply 77  loadvoid 93  loada1 1  shift 46  boolean? 39  atom? 24  cdr 13  brne.l 83  / 58  loadf 31  equal? 52  apply 54  dup 11  loadt 20  jmp.l 48  null? 38  not 35  = 60  set-cdr! 30  eq? 33  * 57  load1 27  bound? 42  brf 3  function? 44  box.l 91  < 28  brnn.l 84  jmp 16  loadv 2  for 76  lvargc 80  dummy_eof 95  + 55  brne 19  compare 61  neg 37  loadv.l 67  number? 40  vargc 74  brn 85  brbound 88  vector 63  loadc1 22  setg.l 72  cons? 18  brf.l 49  aref 92  symbol? 34  aset! 64  car 12  cons 32  tcall.l 82  - 56  brn.l 86  optargs 87  closure 14  pop 4  eqv? 51  list 53  seta 15  seta.l 73  brnn 26  loadnil 65  loadg 7  loada 8  tcall 6)
-	    __init_globals #fn("5000n020w1422w3424w5476w7478w947:w;:" #(#fn("6000n0702161:" #(princ
-  "#;> ")) *prompt* "/" *directory-separator* "\n" *linefeed* *stdout* *output-stream* *stdin*
-									*input-stream* *stderr*
-									*error-stream*) __init_globals)
-	    __rcscript #fn("<000n0708421c360O@A08422c37023@4024^125845185;3=042685772853863B02986513907:8661:O:" #(*os-name*
-  "unknown" "plan9" "home" "HOME" #fn(os-getenv) #fn(string)
-  *directory-separator* ".flisprc" #fn(path-exists?) load) __rcscript)
+	    __init_globals #fn("7000n07021l237022@402384w4^1425w6427w8479w:47;w<47=w>:" #(*os-name*
+  "macos" #fn("6000n0702161:" #(princ "\e[0m\e[1m#;> \e[0m"))
+  #fn("6000n0702161:" #(princ "#;> ")) *prompt* "/" *directory-separator* "\n" *linefeed* *stdout*
+  *output-stream* *stdin* *input-stream* *stderr* *error-stream*) __init_globals)
+	    __rcscript #fn("=000n0708421c360O@T08422c37023@G08424c3=07526514O@4027^184;390428845185;3=0429857:2;53863B02<86513907=8661:O:" #(*os-name*
+  "unknown" "plan9" "home" "macos" princ "\e]0;femtolisp v0.999\a" "HOME" #fn(os-getenv)
+  #fn(string) *directory-separator* ".flisprc" #fn(path-exists?) load) __rcscript)
 	    __script #fn("6000n1200>121{:" #(#fn("6000n070A61:" #(load))
 					     #fn("6000n170051421K61:" #(top-level-exception-handler
 									#fn(exit)))) __script)
binary files a/flisp.boot.builtin b/flisp.boot.builtin differ
--- a/flisp.c
+++ b/flisp.c
@@ -45,7 +45,7 @@
 	builtin_t fptr;
 }builtinspec_t;
 
-#ifdef NDEBUG
+#if defined(NDEBUG)
 __thread
 #endif
 Fl *fl;
@@ -1163,7 +1163,7 @@
 	if(__unlikely(nargs < 2))
 		lerrorf(FL_ArgError, "too few arguments");
 	intptr_t argSP = args-FL(stack);
-	assert(argSP >= 0 && argSP < FL(nstack));
+	assert(argSP >= 0 && argSP < (intptr_t)FL(nstack));
 	while(FL(sp)+2+1+nargs >= FL(nstack))
 		grow_stack();
 	uint32_t k = FL(sp);
@@ -1198,7 +1198,7 @@
 	if(__unlikely(nargs < 2))
 		lerrorf(FL_ArgError, "too few arguments");
 	intptr_t argSP = args-FL(stack);
-	assert(argSP >= 0 && argSP < FL(nstack));
+	assert(argSP >= 0 && argSP < (intptr_t)FL(nstack));
 	if(FL(sp)+1+2*nargs >= FL(nstack))
 		grow_stack();
 	for(size_t n = 0;; n++){
@@ -1284,25 +1284,39 @@
 
 // initialization -------------------------------------------------------------
 
-void
+int
 fl_init(size_t initial_heapsize)
 {
 	int i;
 
-	fl = MEM_CALLOC(1, sizeof(*fl));
+	if((fl = MEM_CALLOC(1, sizeof(*fl))) == nil)
+		return -1;
 	FL(scr_width) = 100;
 
 	FL(heapsize) = initial_heapsize;
 
-	FL(fromspace) = MEM_ALLOC(FL(heapsize));
-	FL(tospace)   = MEM_ALLOC(FL(heapsize));
+	if((FL(fromspace) = MEM_ALLOC(FL(heapsize))) == nil){
+failed:
+		MEM_FREE(FL(fromspace));
+		MEM_FREE(FL(tospace));
+		MEM_FREE(FL(consflags));
+		MEM_FREE(FL(stack));
+		htable_free(&FL(printconses));
+		MEM_FREE(fl);
+		return -1;
+	}
+	if((FL(tospace) = MEM_ALLOC(FL(heapsize))) == nil)
+		goto failed;
+	if((FL(consflags) = bitvector_new(FL(heapsize)/sizeof(cons_t), 1)) == nil)
+		goto failed;
+	if((htable_new(&FL(printconses), 32)) == nil)
+		goto failed;
 	FL(curheap) = FL(fromspace);
 	FL(lim) = FL(curheap)+FL(heapsize)-sizeof(cons_t);
-	FL(consflags) = bitvector_new(FL(heapsize)/sizeof(cons_t), 1);
-	htable_new(&FL(printconses), 32);
-	comparehash_init();
 	FL(nstack) = 4096;
-	FL(stack) = MEM_ALLOC(FL(nstack)*sizeof(value_t));
+	if((FL(stack) = MEM_ALLOC(FL(nstack)*sizeof(value_t))) == nil)
+		goto failed;
+	comparehash_init();
 
 	FL_lambda = symbol("λ", false);
 	FL_function = symbol("function", false);
@@ -1383,6 +1397,7 @@
 	table_init();
 	iostream_init();
 	compress_init();
+	return 0;
 }
 
 // top level ------------------------------------------------------------------
--- a/flisp.h
+++ b/flisp.h
@@ -1,6 +1,10 @@
 #pragma once
 
 #include "platform.h"
+#ifndef __plan9__
+#include "mp.h"
+#include "utf.h"
+#endif
 #include "utf8.h"
 #include "ios.h"
 #include "tbl.h"
@@ -43,11 +47,13 @@
 #define FIXNUM_BITS 62
 #define TOP_BIT (1ULL<<63)
 #define T_FIXNUM T_INT64
+#define PRIdFIXNUM PRId64
 #else
 typedef int32_t fixnum_t;
 #define FIXNUM_BITS 30
 #define TOP_BIT (1U<<31)
 #define T_FIXNUM T_INT32
+#define PRIdFIXNUM PRId32
 #endif
 
 #define ALIGNED(x, sz) (((x) + (sz-1)) & (-sz))
@@ -170,7 +176,7 @@
 #define POP() (FL(stack)[--FL(sp)])
 
 bool isbuiltin(value_t x);
-void fl_init(size_t initial_heapsize);
+int fl_init(size_t initial_heapsize);
 int fl_load_system_image(value_t ios);
 
 _Noreturn void fl_exit(int status);
@@ -393,8 +399,6 @@
 	bool exiting;
 	bool grew;
 
-	fltype_t *fsotype;
-
 	uint32_t *consflags;
 	size_t gccalls;
 
@@ -415,7 +419,7 @@
 };
 
 extern
-#ifdef NDEBUG
+#if defined(NDEBUG)
 __thread
 #endif
 Fl *fl;
--- a/flmain.c
+++ b/flmain.c
@@ -39,7 +39,15 @@
 	ios_init_stdstreams();
 	mpsetminbits(sizeof(fixnum_t)*8);
 
-	fl_init(2*1024*1024);
+#if defined(__macos__)
+	if(fl_init(INITIAL_HEAP_SIZE) != 0){
+		fprintf(stderr, "fl_init failed\n");
+		getchar();
+#else
+	if(fl_init(2*1024*1024) != 0){
+#endif
+		exit(1);
+	}
 
 	value_t f = cvalue(FL(iostreamtype), (int)sizeof(ios_t));
 	ios_t *s = value2c(ios_t*, f);
--- a/htable.c
+++ b/htable.c
@@ -16,9 +16,9 @@
 		size = nextipow2(size);
 		size *= 2; // 2 pointers per key/value pair
 		size *= 2; // aim for 50% occupancy
-		h->table = MEM_ALLOC(size * sizeof(*h->table));
+		if((h->table = MEM_ALLOC(size * sizeof(*h->table))) == nil)
+			return nil;
 	}
-	assert(h->table != nil);
 	h->size = size;
 	h->i = 0;
 	for(size_t i = 0; i < size; i++)
--- a/ios.c
+++ b/ios.c
@@ -22,7 +22,7 @@
 	return nil;
 }
 
-#if !defined(__plan9__)
+#if !defined(__plan9__) && !defined(__macos__)
 static int
 _enonfatal(int err)
 {
@@ -39,7 +39,7 @@
 	ssize_t r;
 
 	while(1){
-#if !defined(__plan9__)
+#if !defined(__plan9__) && !defined(__macos__)
 		errno = 0;
 #endif
 		r = read(fd, buf, n);
@@ -47,7 +47,7 @@
 			*nread = (size_t)r;
 			break;
 		}
-#if defined(__plan9__)
+#if defined(__plan9__) || defined(__macos__)
 		return r;
 #else
 		if(!_enonfatal(errno)){
@@ -84,7 +84,7 @@
 	ssize_t r;
 
 	while(1){
-#if !defined(__plan9__)
+#if !defined(__plan9__) && !defined(__macos__)
 		errno = 0;
 #endif
 		r = write(fd, buf, n);
@@ -92,7 +92,7 @@
 			*nwritten = (size_t)r;
 			break;
 		}
-#if defined(__plan9__)
+#if defined(__plan9__) || defined(__macos__)
 		return r;
 #else
 		if(!_enonfatal(errno)){
@@ -112,6 +112,7 @@
 
 	*nwritten = 0;
 	while(n > 0){
+		wrote = 0;
 		int err = _os_write(fd, buf, n, &wrote);
 		n -= wrote;
 		*nwritten += wrote;
@@ -240,6 +241,7 @@
 		s->state = bst_rd;
 
 		s->fpos = -1;
+		got = 0;
 		if(n > MOST_OF(s->maxsize)){
 			// doesn't fit comfortably in buffer; go direct
 			if(all)
@@ -906,7 +908,7 @@
 		return 1;
 	if(s->_eof)
 		return IOS_EOF;
-#if defined(__plan9__)
+#if defined(__plan9__) || defined(__macos__)
 	USED(ws);
 	return 1; // FIXME(sigrid): wait for input, but not too much
 #else
--- /dev/null
+++ b/macos/platform.h
@@ -1,0 +1,57 @@
+#define _XOPEN_SOURCE 700
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <wctype.h>
+#include <wchar.h>
+
+#define __os_name__ "macos"
+
+#define MEM_CALLOC(n, sz) calloc((size_t)(n), (size_t)(sz))
+#define MEM_ALLOC(n) malloc((size_t)(n))
+#define MEM_REALLOC(p, n) realloc((p), (size_t)(n))
+#define MEM_FREE(x) free(x)
+#define MEM_STRDUP(s) strdup(s)
+
+#define sadd_overflow __builtin_add_overflow
+#define sadd_overflow_64 __builtin_add_overflow
+#define smul_overflow_64 __builtin_mul_overflow
+
+#define nil NULL
+#define USED(x) ((void)(x))
+#define nelem(x) (int)(sizeof(x)/sizeof((x)[0]))
+
+#ifdef __GNUC__
+#define __unlikely(x) __builtin_expect(!!(x), 0)
+#define __likely(x) __builtin_expect(!!(x), 1)
+#define __printfmt(x, y) __attribute__((format(printf, x, y)))
+#endif
+
+#define PATHSEP '/'
+#define PATHSEPSTRING "/"
+#define PATHLISTSEP ':'
+#define PATHLISTSEPSTRING ":"
+#define ISPATHSEP(c) ((c) == '/')
+
+#ifndef BYTE_ORDER
+#error unknown byte order
+#endif
--- a/meson.build
+++ b/meson.build
@@ -42,6 +42,29 @@
 	language: 'c',
 )
 
+platform = 'posix'
+
+if host_machine.system() == 'macos'
+	platform = 'macos'
+	add_languages(
+		'cpp',
+		native: false,
+		required: true,
+	)
+	cpp = meson.get_compiler('cpp')
+	add_project_arguments(
+		'-DINITIAL_HEAP_SIZE=64*1024',
+		language: 'c',
+	)
+	extras = [cpp.find_library('RetroConsole', required: true)]
+else
+	add_project_arguments(
+		'-DINITIAL_HEAP_SIZE=2*1024*1024',
+		language: 'c',
+	)
+	extras = []
+endif
+
 src = [
 	'3rd/fn.c',
 	'3rd/mt19937-64.c',
@@ -66,7 +89,7 @@
 	'random.c',
 	'read.c',
 	'string.c',
-	'sys_posix.c',
+	'sys_' + platform + '.c',
 	'table.c',
 	'types.c',
 	'utf8.c',
@@ -128,7 +151,7 @@
 	],
 	include_directories: include_directories(
 		'3rd/brieflz',
-		'posix',
+		platform,
 	),
 )
 
@@ -169,7 +192,7 @@
 	],
 	include_directories: include_directories(
 		'3rd/mp',
-		'posix',
+		platform,
 	),
 )
 
@@ -183,7 +206,7 @@
 	],
 	include_directories: include_directories(
 		'3rd/utf',
-		'posix',
+		platform,
 	),
 )
 
@@ -196,13 +219,13 @@
 	],
 	dependencies: [
 		math,
-	],
+	] + extras,
 	include_directories: include_directories(
 		'3rd',
 		'3rd/brieflz',
 		'3rd/mp',
 		'3rd/utf',
-		'posix',
+		platform,
 	),
 	link_with: [
 		brieflz,
@@ -211,37 +234,78 @@
 	],
 )
 
-mptest = executable(
-	'mptest',
-	sources: [
-		'3rd/mp/test.c',
-	],
-	include_directories: include_directories(
-		'posix',
-	),
-	link_with: [
-		mp,
-	],
-)
-test('mp', mptest)
+if platform == 'macos'
+	flisp_pef = custom_target(
+		'flisp.pef',
+		input: [
+			flisp
+		],
+		output: [
+			'flisp.pef',
+		],
+		command: [
+			meson.get_external_property('makepef'), '@INPUT@', '-o', '@OUTPUT@',
+		],
+	)
+	flisp_bin = custom_target(
+		'flisp.bin',
+		input: [
+			flisp_pef,
+		],
+		output: [
+			'flisp.bin',
+		],
+		command: [
+			meson.get_external_property('rez'),
+			'-I' + meson.get_external_property('rincludes'),
+			meson.get_external_property('rincludes') + '/RetroPPCAPPL.r',
+			'-DCFRAG_NAME="flisp"',
+			'-o', '@OUTPUT@',
+			#'--cc', 'flisp.dsk',
+			#'--cc', 'flisp.APPL',
+			#'--cc', '%flisp.ad',
+			'-t', 'APPL',
+			'-c', 'SGRD',
+			'--data', '@INPUT@',
+		],
+		build_by_default: true,
+	)
+endif
 
-mptest2 = executable(
-	'mptest2',
-	sources: [
-		'3rd/mp/test/convtest.c',
-		'3rd/mp/test/gen.tab.c',
-		'3rd/mp/test/ld.c',
-		'3rd/mp/test/main.c',
-	],
-	include_directories: include_directories(
-		'3rd/mp/test',
-		'posix',
-	),
-	link_with: [
-		mp,
-	],
-)
-test('mp2', mptest2, timeout: -1)
+build_tests = get_option('build-tests')
+if build_tests.enabled()
+	mptest = executable(
+		'mptest',
+		sources: [
+			'3rd/mp/test.c',
+		],
+		include_directories: include_directories(
+			platform,
+		),
+		link_with: [
+			mp,
+		],
+	)
+	test('mp', mptest)
+	mptest2 = executable(
+		'mptest2',
+		sources: [
+			'3rd/mp/test/convtest.c',
+			'3rd/mp/test/gen.tab.c',
+			'3rd/mp/test/ld.c',
+			'3rd/mp/test/main.c',
+		],
+		include_directories: include_directories(
+			'3rd/mp',
+			'3rd/mp/test',
+			platform,
+		),
+		link_with: [
+			mp,
+		],
+	)
+	test('mp2', mptest2, timeout: -1)
+endif
 
 tests_dir = join_paths(meson.current_source_dir(), 'test')
 
--- a/meson.options
+++ b/meson.options
@@ -1,0 +1,1 @@
+option('build-tests', type: 'feature', value: 'enabled')
--- a/plan9/platform.h
+++ b/plan9/platform.h
@@ -82,8 +82,10 @@
 #define isfinite(d) (((*(uint64_t*)&(d))&0x7ff0000000000000ULL) != 0x7ff0000000000000ULL)
 
 #define PRIu32 "ud"
+#define PRIx32 "x"
 #define PRId64 "lld"
 #define PRIu64 "llud"
+#define PRIx64 "llx"
 
 #define INT32_MAX 0x7fffffff
 #define UINT32_MAX 0xffffffffU
--- a/posix/mp.h
+++ /dev/null
@@ -1,178 +1,0 @@
-#pragma once
-
-#ifdef BITS64
-typedef uint64_t mpdigit;
-#else
-typedef uint32_t mpdigit;
-#endif
-
-typedef union FPdbleword FPdbleword;
-union FPdbleword
-{
-	double	x;
-	struct {
-#if BYTE_ORDER == LITTLE_ENDIAN
-		uint32_t lo;
-		uint32_t hi;
-#else
-		uint32_t hi;
-		uint32_t lo;
-#endif
-	};
-};
-
-#define mpdighi  (((mpdigit)1)<<(Dbits-1))
-#define DIGITS(x) ((int)(x) >= -(Dbits-1) ? ((Dbits - 1 + (x))/Dbits) : 0)
-
-extern	int	dec16(uint8_t*, int, char*, int);
-extern	int	enc16(char*, int, uint8_t*, int);
-extern	mpdigit	dec16chr(int);
-extern	int	enc16chr(int);
-
-/*
- * the code assumes mpdigit to be at least an int
- * mpdigit must be an atomic type.  mpdigit is defined
- * in the architecture specific u.h
- */
-typedef struct mpint mpint;
-
-struct mpint
-{
-	mpdigit	*p;
-	uint32_t	size;	/* allocated digits */
-	uint32_t	top;	/* significant digits */
-	int	sign;	/* +1 or -1 */
-	int	flags;
-};
-
-enum
-{
-	MPstatic=	0x01,	/* static constant */
-	MPnorm=		0x02,	/* normalization status */
-	MPtimesafe=	0x04,	/* request time invariant computation */
-
-	Dbytes=		sizeof(mpdigit),	/* bytes per digit */
-	Dbits=		Dbytes*8		/* bits per digit */
-};
-
-/* allocation */
-void	mpsetminbits(int n);	/* newly created mpint's get at least n bits */
-mpint*	mpnew(int n);		/* create a new mpint with at least n bits */
-void	mpfree(mpint *b);
-void	mpbits(mpint *b, int n);	/* ensure that b has at least n bits */
-mpint*	mpnorm(mpint *b);		/* dump leading zeros */
-mpint*	mpcopy(mpint *b);
-void	mpassign(mpint *old, mpint *new);
-
-/* random bits */
-mpint*	mprand(int bits, void (*gen)(uint8_t*, int), mpint *b);
-/* return uniform random [0..n-1] */
-mpint*	mpnrand(mpint *n, void (*gen)(uint8_t*, int), mpint *b);
-
-/* conversion */
-mpint*	strtomp(const char*, char**, int, mpint*);	/* ascii */
-char*	mptoa(mpint*, int, char*, int);
-mpint*	letomp(uint8_t*, uint32_t, mpint*);	/* byte array, little-endian */
-int	mptole(mpint*, uint8_t*, uint32_t, uint8_t**);
-void	mptolel(mpint *b, uint8_t *p, int n);
-mpint*	betomp(uint8_t*, uint32_t, mpint*);	/* byte array, big-endian */
-int	mptobe(mpint*, uint8_t*, uint32_t, uint8_t**);
-void	mptober(mpint *b, uint8_t *p, int n);
-uint32_t	mptoui(mpint*);			/* unsigned int */
-mpint*	uitomp(uint32_t, mpint*);
-int	mptoi(mpint*);			/* int */
-mpint*	itomp(int, mpint*);
-uint64_t	mptouv(mpint*);			/* unsigned int64_t */
-mpint*	uvtomp(uint64_t, mpint*);
-int64_t	mptov(mpint*);			/* int64_t */
-mpint*	vtomp(int64_t, mpint*);
-double	mptod(mpint*);			/* double */
-mpint*	dtomp(double, mpint*);
-
-/* divide the 2 digit dividend by the one digit divisor and stick in quotient */
-/* we assume that the result is one digit - overflow is all 1's */
-void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
-
-/* in the following, the result mpint may be */
-/* the same as one of the inputs. */
-void	mpadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
-void	mpsub(mpint *b1, mpint *b2, mpint *diff);	/* diff = b1-b2 */
-void	mpleft(mpint *b, int shift, mpint *res);	/* res = b<<shift */
-void	mpright(mpint *b, int shift, mpint *res);	/* res = b>>shift */
-void	mpmul(mpint *b1, mpint *b2, mpint *prod);	/* prod = b1*b2 */
-void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	/* res = b**e mod m */
-void	mpmod(mpint *b, mpint *m, mpint *remainder);	/* remainder = b mod m */
-
-/* logical operations */
-void	mpand(mpint *b1, mpint *b2, mpint *res);
-void	mpbic(mpint *b1, mpint *b2, mpint *res);
-void	mpor(mpint *b1, mpint *b2, mpint *res);
-void	mpnot(mpint *b, mpint *res);
-void	mpxor(mpint *b1, mpint *b2, mpint *res);
-void	mpasr(mpint *b, int shift, mpint *res);
-
-/* modular arithmetic, time invariant when 0≤b1≤m-1 and 0≤b2≤m-1 */
-void	mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum);	/* sum = b1+b2 % m */
-void	mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff);	/* diff = b1-b2 % m */
-void	mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod);	/* prod = b1*b2 % m */
-
-/* quotient = dividend/divisor, remainder = dividend % divisor */
-void	mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
-
-/* return neg, 0, pos as b1-b2 is neg, 0, pos */
-int	mpcmp(mpint *b1, mpint *b2);
-
-/* res = s != 0 ? b1 : b2 */
-void	mpsel(int s, mpint *b1, mpint *b2, mpint *res);
-
-/* extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d */
-void	mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
-
-/* res = b**-1 mod m */
-void	mpinvert(mpint *b, mpint *m, mpint *res);
-
-/* bit counting */
-uint32_t	mpsignif(mpint*);	/* number of sigificant bits in mantissa */
-uint32_t	mplowbits0(mpint*);	/* k, where n = 2**k * q for odd q */
-
-/* well known constants */
-extern mpint	*mpzero, *mpone, *mptwo;
-
-/* sum[0:alen] = a[0:alen-1] + b[0:blen-1] */
-/* prereq: alen >= blen, sum has room for alen+1 digits */
-void	mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
-
-/* diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] */
-/* prereq: alen >= blen, diff has room for alen digits */
-void	mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
-
-/* p[0:n] += m * b[0:n-1] */
-/* prereq: p has room for n+1 digits */
-void	mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
-
-/* p[0:n] -= m * b[0:n-1] */
-/* prereq: p has room for n+1 digits */
-int	mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
-
-/* p[0:alen+blen-1] = a[0:alen-1] * b[0:blen-1] */
-/* prereq: alen >= blen, p has room for m*n digits */
-void	mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
-void	mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
-
-/* sign of a - b or zero if the same */
-int	mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
-int	mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen);
-
-/* playing with magnitudes */
-int	mpmagcmp(mpint *b1, mpint *b2);
-void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
-void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
-
-/* fast field arithmetic */
-typedef struct Mfield	Mfield;
-
-struct Mfield
-{
-	mpint m;
-	int	(*reduce)(Mfield*, mpint*, mpint*);
-};
--- a/posix/platform.h
+++ b/posix/platform.h
@@ -76,6 +76,3 @@
 #define BIG_ENDIAN __BIG_ENDIAN
 #define BYTE_ORDER __BYTE_ORDER
 #endif
-
-#include "mp.h"
-#include "utf.h"
--- a/posix/utf.h
+++ /dev/null
@@ -1,27 +1,0 @@
-#pragma once
-
-enum {
-	Runeself = 0x80,
-	Runeerror = 0xfffd,
-	Runemax = 0x10ffff,
-	UTFmax = 4,
-};
-
-typedef uint32_t Rune;
-
-int chartorune(Rune *rune, const char *str);
-int runetochar(char *str, const Rune *rune);
-int runenlen(const Rune *r, int nrune);
-int fullrune(const char *str, int n);
-int runelen(Rune c);
-Rune tolowerrune(Rune c);
-Rune toupperrune(Rune c);
-Rune totitlerune(Rune c);
-int islowerrune(Rune c);
-int isupperrune(Rune c);
-int isalpharune(Rune c);
-int istitlerune(Rune c);
-int isspacerune(Rune c);
-int isdigitrune(Rune c);
-
-int utfnlen(const char *s, long m);
--- a/print.c
+++ b/print.c
@@ -399,7 +399,7 @@
 
 	switch(tag(v)){
 	case TAG_NUM: case TAG_NUM1:
-		FL(hpos) += ios_printf(f, "%"PRId64, (int64_t)numval(v));
+		FL(hpos) += ios_printf(f, "%"PRIdFIXNUM, numval(v));
 		break;
 	case TAG_SYM:
 		name = symbol_name(v);
@@ -667,7 +667,7 @@
 				if(u8_iswprint(r))
 					outs(f, seq);
 				else
-					FL(hpos) += ios_printf(f, "x%04x", r);
+					FL(hpos) += ios_printf(f, "x%04"PRIx32, r);
 				break;
 			}
 		}
--- /dev/null
+++ b/sys_macos.c
@@ -1,0 +1,81 @@
+#include "flisp.h"
+#include "timefuncs.h"
+
+double
+sec_realtime(void)
+{
+	struct timeval now;
+
+	if(gettimeofday(&now, nil) != 0)
+		return 0.0;
+	return (double)now.tv_sec + (double)now.tv_usec/1.0e6;
+}
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uint64_t
+nanosec_monotonic(void)
+{
+	return 0;
+}
+
+void
+timestring(double s, char *buf, int sz)
+{
+	USED(s); USED(sz);
+	buf[0] = 0;
+}
+
+double
+parsetime(const char *s)
+{
+	USED(s);
+	return 0.0;
+}
+
+void
+sleep_ms(int ms)
+{
+	USED(ms);
+}
+
+int
+ftruncate(int f, off_t sz)
+{
+	USED(f); USED(sz);
+	return -1;
+}
+
+char *
+getcwd(char *buf, size_t len)
+{
+	USED(buf); USED(len);
+	return nil;
+}
+
+int
+chdir(const char *path)
+{
+	USED(path);
+	return -1;
+}
+
+int
+access(const char *path, int amode)
+{
+	USED(path); USED(amode);
+	return -1;
+}
+
+static const uint8_t boot[] = {
+#include "flisp.boot.h"
+};
+
+int
+main(int argc, char **argv)
+{
+	flmain(boot, sizeof(boot), argc, argv);
+}
--- a/system.lsp
+++ b/system.lsp
@@ -1114,9 +1114,12 @@
 
 ; initialize globals that need to be set at load time
 (define (__init_globals)
-  (set! *prompt*
-    "Function called by REPL to signal the user input is required.
-Default function prints \"#;> \"." (λ () (princ "#;> ")))
+  (let ((defprompt (if (= *os-name* "macos")
+                       (λ () (princ "\x1b[0m\x1b[1m#;> \x1b[0m"))
+                       (λ () (princ "#;> ")))))
+    (set! *prompt*
+      "Function called by REPL to signal the user input is required.
+Default function prints \"#;> \"." defprompt))
   (set! *directory-separator* "/")
   (set! *linefeed* "\n")
   (set! *output-stream* *stdout*)
@@ -1132,8 +1135,9 @@
   (let* ((homevar (case *os-name*
                     (("unknown") #f)
                     (("plan9") "home")
+                    (("macos") (princ "\x1b]0;femtolisp v0.999\007") #f)
                     (else "HOME")))
-         (home (os-getenv homevar))
+         (home (and homevar (os-getenv homevar)))
          (fname (and home (string home *directory-separator* ".flisprc"))))
     (when (and fname (path-exists? fname)) (load fname))))
 
--- a/utf8.c
+++ b/utf8.c
@@ -129,7 +129,7 @@
 int
 u8_escape_rune(char *buf, size_t sz, Rune ch)
 {
-	assert(sz > 2);
+	assert(sz > 12);
 	if(ch >= 0x20 && ch < 0x7f){
 		buf[0] = ch;
 		buf[1] = '\0';
@@ -136,9 +136,9 @@
 		return 1;
 	}
 	if(ch > 0xffff)
-		return snprintf(buf, sz, "\\U%.8x", ch);
+		return snprintf(buf, sz, "\\U%.8"PRIx32, ch);
 	if(ch >= Runeself)
-		return snprintf(buf, sz, "\\u%04x", ch);
+		return snprintf(buf, sz, "\\u%04"PRIx32, ch);
 	switch(ch){
 	case '\n': return buf_put2c(buf, "\\n");
 	case '\t': return buf_put2c(buf, "\\t");
@@ -150,7 +150,7 @@
 	case '\r': return buf_put2c(buf, "\\r");
 	case '\v': return buf_put2c(buf, "\\v");
 	}
-	return snprintf(buf, sz, "\\x%02x", ch);
+	return snprintf(buf, sz, "\\x%02"PRIx32, ch);
 }
 
 size_t