shithub: femtolisp

Download patch

ref: 17f2f68fb46834325bf81ae2b05907c89a7ec14d
parent: 02fe9797be10453dd75761640bace0e809cf7182
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Nov 7 21:49:05 EST 2024

move all global context to a single struct in tls

--- a/builtins.c
+++ b/builtins.c
@@ -23,9 +23,9 @@
 BUILTIN("nconc", nconc)
 {
 	if(nargs == 0)
-		return FL_NIL;
+		return fl->FL_NIL;
 
-	value_t lst, first = FL_NIL;
+	value_t lst, first = fl->FL_NIL;
 	value_t *pcdr = &first;
 	cons_t *c;
 	int i = 0;
@@ -40,7 +40,7 @@
 			while(iscons(c->cdr))
 				c = (cons_t*)ptr(c->cdr);
 			pcdr = &c->cdr;
-		}else if(lst != FL_NIL)
+		}else if(lst != fl->FL_NIL)
 			type_error("cons", lst);
 	}
 	*pcdr = lst;
@@ -61,7 +61,7 @@
 			return bind;
 		v = cdr_(v);
 	}
-	return FL_F;
+	return fl->FL_F;
 }
 
 BUILTIN("memq", memq)
@@ -74,7 +74,7 @@
 		if((c = ptr(v))->car == args[0])
 			return v;
 	}
-	return FL_F;
+	return fl->FL_F;
 }
 
 BUILTIN("length", length)
@@ -86,7 +86,7 @@
 
 	if(isvector(a))
 		return fixnum(vector_size(a));
-	if(a == FL_NIL)
+	if(a == fl->FL_NIL)
 		return fixnum(0);
 	if(iscons(a)){
 		size_t n = 0;
@@ -105,9 +105,9 @@
 	}
 	if(iscprim(a)){
 		cv = (cvalue_t*)ptr(a);
-		if(cp_class(cv) == bytetype)
+		if(cp_class(cv) == fl->bytetype)
 			return fixnum(1);
-		if(cp_class(cv) == runetype)
+		if(cp_class(cv) == fl->runetype)
 			return fixnum(runelen(*(Rune*)cp_data(cv)));
 	}
 	if(iscvalue(a) && cv_class(ptr(a))->eltype != nil)
@@ -140,7 +140,7 @@
 {
 	argcount(nargs, 1);
 	return (issymbol(args[0]) &&
-			iskeyword((symbol_t*)ptr(args[0]))) ? FL_T : FL_F;
+			iskeyword((symbol_t*)ptr(args[0]))) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("top-level-value", top_level_value)
@@ -172,32 +172,28 @@
 	}
 }
 
-extern symbol_t *symtab;
-
 BUILTIN("environment", environment)
 {
 	USED(args);
 	argcount(nargs, 0);
-	value_t lst = FL_NIL;
+	value_t lst = fl->FL_NIL;
 	fl_gc_handle(&lst);
-	global_env_list(symtab, &lst);
+	global_env_list(fl->symtab, &lst);
 	fl_free_gc_handles(1);
 	return lst;
 }
 
-extern value_t QUOTE;
-
 BUILTIN("constant?", constantp)
 {
 	argcount(nargs, 1);
 	if(issymbol(args[0]))
-		return isconstant((symbol_t*)ptr(args[0])) ? FL_T : FL_F;
+		return isconstant((symbol_t*)ptr(args[0])) ? fl->FL_T : fl->FL_F;
 	if(iscons(args[0])){
-		if(car_(args[0]) == QUOTE)
-			return FL_T;
-		return FL_F;
+		if(car_(args[0]) == fl->QUOTE)
+			return fl->FL_T;
+		return fl->FL_F;
 	}
-	return FL_T;
+	return fl->FL_T;
 }
 
 BUILTIN("integer-valued?", integer_valuedp)
@@ -205,11 +201,11 @@
 	argcount(nargs, 1);
 	value_t v = args[0];
 	if(isfixnum(v))
-		return FL_T;
+		return fl->FL_T;
 	if(iscprim(v)){
 		numerictype_t nt = cp_numtype((cprim_t*)ptr(v));
 		if(nt < T_FLOAT)
-			return FL_T;
+			return fl->FL_T;
 		void *data = cp_data((cprim_t*)ptr(v));
 		if(nt == T_FLOAT){
 			float f = *(float*)data;
@@ -216,7 +212,7 @@
 			if(f < 0)
 				f = -f;
 			if(f <= FLT_MAXINT && (float)(int32_t)f == f)
-				return FL_T;
+				return fl->FL_T;
 		}else{
 			assert(nt == T_DOUBLE);
 			double d = *(double*)data;
@@ -223,10 +219,10 @@
 			if(d < 0)
 				d = -d;
 			if(d <= DBL_MAXINT && (double)(int64_t)d == d)
-				return FL_T;
+				return fl->FL_T;
 		}
 	}
-	return FL_F;
+	return fl->FL_F;
 }
 
 BUILTIN("integer?", integerp)
@@ -235,7 +231,7 @@
 	value_t v = args[0];
 	return (isfixnum(v) ||
 			(iscprim(v) && cp_numtype((cprim_t*)ptr(v)) < T_FLOAT)) ?
-		FL_T : FL_F;
+		fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("bignum?", bignump)
@@ -243,7 +239,7 @@
 	argcount(nargs, 1);
 	value_t v = args[0];
 	return (iscvalue(v) && cp_numtype((cprim_t*)ptr(v)) == T_MPINT) ?
-		FL_T : FL_F;
+		fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("fixnum", fixnum)
@@ -295,11 +291,11 @@
 		argcount(nargs, 1);
 	i = toulong(args[0]);
 	if(i < 0)
-		lerrorf(ArgError, "invalid size: %d", i);
+		lerrorf(fl->ArgError, "invalid size: %d", i);
 	v = alloc_vector((unsigned)i, 0);
 	a = 1;
 	for(k = 0; k < i; k++){
-		f = a < nargs ? args[a] : FL_UNSPECIFIED;
+		f = a < nargs ? args[a] : fl->FL_UNSPECIFIED;
 		vector_elt(v, k) = f;
 		if((a = (a + 1) % nargs) < 1)
 			a = 1;
@@ -358,8 +354,8 @@
 	}
 	char *ptr = tostring(args[0]);
 	if(chdir(ptr))
-		lerrorf(IOError, "could not cd to %s", ptr);
-	return FL_T;
+		lerrorf(fl->IOError, "could not cd to %s", ptr);
+	return fl->FL_T;
 }
 
 BUILTIN("path-exists?", path_existsp)
@@ -366,7 +362,7 @@
 {
 	argcount(nargs, 1);
 	char *path = tostring(args[0]);
-	return access(path, F_OK) == 0 ? FL_T : FL_F;
+	return access(path, F_OK) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("os-getenv", os_getenv)
@@ -375,9 +371,9 @@
 	char *name = tostring(args[0]);
 	char *val = getenv(name);
 	if(val == nil)
-		return FL_F;
+		return fl->FL_F;
 	if(*val == 0)
-		return symbol_value(emptystringsym);
+		return symbol_value(fl->emptystringsym);
 	return cvalue_static_cstring(val);
 }
 
@@ -386,7 +382,7 @@
 	argcount(nargs, 2);
 	char *name = tostring(args[0]);
 	int result;
-	if(args[1] == FL_F)
+	if(args[1] == fl->FL_F)
 		result = unsetenv(name);
 	else{
 		char *val = tostring(args[1]);
@@ -393,8 +389,8 @@
 		result = setenv(name, val, 1);
 	}
 	if(result != 0)
-		lerrorf(ArgError, "invalid environment variable");
-	return FL_T;
+		lerrorf(fl->ArgError, "invalid environment variable");
+	return fl->FL_T;
 }
 
 BUILTIN("rand", rand)
--- a/cvalues.c
+++ b/cvalues.c
@@ -9,48 +9,20 @@
 // trigger unconditional GC after this many bytes are allocated
 #define ALLOC_LIMIT_TRIGGER 67108864
 
-value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
-value_t int64sym, uint64sym, bignumsym;
-value_t longsym, ulongsym, bytesym, runesym;
-value_t floatsym, doublesym;
-value_t gftypesym, stringtypesym, runestringtypesym;
-value_t emptystringsym;
-
-value_t structsym, arraysym, enumsym, cfunctionsym, voidsym, pointersym;
-value_t unionsym;
-
-htable_t TypeTable;
-htable_t reverse_dlsym_lookup_table;
-fltype_t *mpinttype;
-static fltype_t *int8type, *uint8type;
-static fltype_t *int16type, *uint16type;
-static fltype_t *int32type, *uint32type;
-static fltype_t *int64type, *uint64type;
-static fltype_t *longtype, *ulongtype;
-static fltype_t *floattype, *doubletype;
-fltype_t *bytetype, *runetype;
-fltype_t *stringtype, *runestringtype;
-fltype_t *builtintype;
-
-static size_t malloc_pressure = 0;
-static cvalue_t **Finalizers = nil;
-static size_t nfinalizers = 0;
-static size_t maxfinalizers = 0;
-
 static void cvalue_init(fltype_t *type, value_t v, void *dest);
 
 void
 add_finalizer(cvalue_t *cv)
 {
-	if(nfinalizers == maxfinalizers){
-		size_t nn = maxfinalizers == 0 ? 256 : maxfinalizers*2;
-		cvalue_t **temp = LLT_REALLOC(Finalizers, nn*sizeof(cvalue_t*));
+	if(fl->nfinalizers == fl->maxfinalizers){
+		size_t nn = fl->maxfinalizers == 0 ? 256 : fl->maxfinalizers*2;
+		cvalue_t **temp = LLT_REALLOC(fl->Finalizers, nn*sizeof(cvalue_t*));
 		if(temp == nil)
-			lerrorf(MemoryError, "out of memory");
-		Finalizers = temp;
-		maxfinalizers = nn;
+			lerrorf(fl->MemoryError, "out of memory");
+		fl->Finalizers = temp;
+		fl->maxfinalizers = nn;
 	}
-	Finalizers[nfinalizers++] = cv;
+	fl->Finalizers[fl->nfinalizers++] = cv;
 }
 
 // remove dead objects from finalization list in-place
@@ -57,8 +29,8 @@
 void
 sweep_finalizers(void)
 {
-	cvalue_t **lst = Finalizers;
-	size_t n = 0, ndel = 0, l = nfinalizers;
+	cvalue_t **lst = fl->Finalizers;
+	size_t n = 0, ndel = 0, l = fl->nfinalizers;
 	cvalue_t *tmp;
 #define SWAP_sf(a, b) (tmp = a, a = b, b = tmp, 1)
 	if(l == 0)
@@ -73,7 +45,7 @@
 			fltype_t *t = cv_class(tmp);
 			if(t->vtable != nil && t->vtable->finalize != nil)
 				t->vtable->finalize(tagptr(tmp, TAG_CVALUE));
-			if(!isinlined(tmp) && owned(tmp) && !fl_exiting){
+			if(!isinlined(tmp) && owned(tmp) && !fl->exiting){
 				memset(cv_data(tmp), 0xbb, cv_len(tmp));
 				LLT_FREE(cv_data(tmp));
 			}
@@ -81,13 +53,13 @@
 		}
 	}while((n < l-ndel) && SWAP_sf(lst[n], lst[n+ndel]));
 
-	nfinalizers -= ndel;
+	fl->nfinalizers -= ndel;
 #ifdef VERBOSEGC
 	if(ndel > 0)
 		printf("GC: finalized %d objects\n", ndel);
 #endif
 
-	malloc_pressure = 0;
+	fl->malloc_pressure = 0;
 }
 
 // compute the size of the metadata object for a cvalue
@@ -135,9 +107,9 @@
 	if(valid_numtype(type->numtype) && type->numtype != T_MPINT)
 		return cprim(type, sz);
 
-	if(type->eltype == bytetype){
+	if(type->eltype == fl->bytetype){
 		if(sz == 0)
-			return symbol_value(emptystringsym);
+			return symbol_value(fl->emptystringsym);
 		sz++;
 		str = 1;
 	}
@@ -149,13 +121,13 @@
 		if(!nofinalize && type->vtable != nil && type->vtable->finalize != nil)
 			add_finalizer(pcv);
 	}else{
-		if(malloc_pressure > ALLOC_LIMIT_TRIGGER)
+		if(fl->malloc_pressure > ALLOC_LIMIT_TRIGGER)
 			gc(0);
 		pcv = alloc_words(CVALUE_NWORDS);
 		pcv->type = type;
 		pcv->data = LLT_ALLOC(sz);
 		autorelease(pcv);
-		malloc_pressure += sz;
+		fl->malloc_pressure += sz;
 	}
 	if(str)
 		((char*)pcv->data)[--sz] = '\0';
@@ -190,7 +162,7 @@
 	pcv->data = ptr;
 	pcv->len = sz;
 	pcv->type = type;
-	if(parent != NIL){
+	if(parent != fl->NIL){
 		pcv->type = (fltype_t*)(((uintptr_t)pcv->type) | CV_PARENT_BIT);
 		pcv->parent = parent;
 	}
@@ -201,13 +173,13 @@
 value_t
 cvalue_string(size_t sz)
 {
-	return cvalue(stringtype, sz);
+	return cvalue(fl->stringtype, sz);
 }
 
 value_t
 cvalue_static_cstring(const char *str)
 {
-	return cvalue_from_ref(stringtype, (char*)str, strlen(str), NIL);
+	return cvalue_from_ref(fl->stringtype, (char*)str, strlen(str), fl->NIL);
 }
 
 value_t
@@ -280,10 +252,10 @@
 	{ \
 		if(nargs == 0){ \
 			PUSH(fixnum(0)); \
-			args = &Stack[SP-1]; \
+			args = &fl->Stack[fl->SP-1]; \
 		} \
-		value_t cp = cprim(typenam##type, sizeof(ctype)); \
-		if(cvalue_##ctype##_init(typenam##type, args[0], cp_data((cprim_t*)ptr(cp)))) \
+		value_t cp = cprim(fl->typenam##type, sizeof(ctype)); \
+		if(cvalue_##ctype##_init(fl->typenam##type, args[0], cp_data((cprim_t*)ptr(cp)))) \
 			type_error("number", args[0]); \
 		return cp; \
 	}
@@ -291,7 +263,7 @@
 #define num_ctor_ctor(typenam, ctype, tag) \
 	value_t mk_##typenam(ctype n) \
 	{ \
-		value_t cp = cprim(typenam##type, sizeof(ctype)); \
+		value_t cp = cprim(fl->typenam##type, sizeof(ctype)); \
 		*(ctype*)cp_data((cprim_t*)ptr(cp)) = n; \
 		return cp; \
 	}
@@ -342,10 +314,10 @@
 {
 	if(nargs == 0){
 		PUSH(fixnum(0));
-		args = &Stack[SP-1];
+		args = &fl->Stack[fl->SP-1];
 	}
-	value_t cv = cvalue(mpinttype, sizeof(mpint*));
-	if(cvalue_mpint_init(mpinttype, args[0], cv_data((cvalue_t*)ptr(cv))))
+	value_t cv = cvalue(fl->mpinttype, sizeof(mpint*));
+	if(cvalue_mpint_init(fl->mpinttype, args[0], cv_data((cvalue_t*)ptr(cv))))
 		type_error("number", args[0]);
 	return cv;
 }
@@ -354,7 +326,7 @@
 value_t
 mk_mpint(mpint *n)
 {
-	value_t cv = cvalue(mpinttype, sizeof(mpint*));
+	value_t cv = cvalue(fl->mpinttype, sizeof(mpint*));
 	*(mpint**)cvalue_data(cv) = n;
 	return cv;
 }
@@ -419,7 +391,7 @@
 				return 0;
 			}
 		}
-		lerrorf(ArgError, "invalid enum value");
+		lerrorf(fl->ArgError, "invalid enum value");
 	}
 	if(isfixnum(arg))
 		n = (int)numval(arg);
@@ -429,7 +401,7 @@
 	}else
 		type_error("number", arg);
 	if((unsigned)n >= vector_size(syms))
-		lerrorf(ArgError, "value out of range");
+		lerrorf(fl->ArgError, "value out of range");
 	*(int*)dest = n;
 	return 0;
 }
@@ -437,7 +409,7 @@
 BUILTIN("enum", enum)
 {
 	argcount(nargs, 2);
-	value_t type = fl_list2(enumsym, args[0]);
+	value_t type = fl_list2(fl->enumsym, args[0]);
 	fltype_t *ft = get_type(type);
 	value_t cv = cvalue(ft, sizeof(int32_t));
 	cvalue_enum_init(ft, args[1], cp_data((cprim_t*)ptr(cv)));
@@ -457,7 +429,7 @@
 		return vector_size(arg);
 	if(iscons(arg))
 		return llength(arg);
-	if(arg == NIL)
+	if(arg == fl->NIL)
 		return 0;
 	if(isarray(arg))
 		return cvalue_arraylen(arg);
@@ -477,7 +449,7 @@
 	if(iscons(cdr_(cdr_(type)))){
 		size_t tc = toulong(car_(cdr_(cdr_(type))));
 		if(tc != cnt)
-			lerrorf(ArgError, "size mismatch");
+			lerrorf(fl->ArgError, "size mismatch");
 	}
 
 	sz = elsize * cnt;
@@ -490,7 +462,7 @@
 		}
 		return 0;
 	}
-	if(iscons(arg) || arg == NIL){
+	if(iscons(arg) || arg == fl->NIL){
 		i = 0;
 		while(iscons(arg)){
 			if(i == cnt){
@@ -503,7 +475,7 @@
 			arg = cdr_(arg);
 		}
 		if(i != cnt)
-			lerrorf(ArgError, "size mismatch");
+			lerrorf(fl->ArgError, "size mismatch");
 		return 0;
 	}
 	if(iscvalue(arg)){
@@ -514,11 +486,11 @@
 				if(cv_len(cv) == sz)
 					memmove(dest, cv_data(cv), sz);
 				else
-					lerrorf(ArgError, "size mismatch");
+					lerrorf(fl->ArgError, "size mismatch");
 				return 0;
 			}else{
 				// TODO: initialize array from different type elements
-				lerrorf(ArgError, "element type mismatch");
+				lerrorf(fl->ArgError, "element type mismatch");
 			}
 		}
 	}
@@ -559,7 +531,7 @@
 		argcount(nargs, 3);
 	cnt = toulong(args[1]);
 	if(cnt < 0)
-		lerrorf(ArgError, "invalid size: %d", cnt);
+		lerrorf(fl->ArgError, "invalid size: %d", cnt);
 
 	fltype_t *type = get_array_type(args[0]);
 	elsize = type->elsz;
@@ -641,29 +613,29 @@
 
 	if(iscons(type)){
 		value_t hed = car_(type);
-		if(hed == structsym)
-			return cvalue_struct_offs(type, NIL, 1, palign);
-		if(hed == unionsym)
+		if(hed == fl->structsym)
+			return cvalue_struct_offs(type, fl->NIL, 1, palign);
+		if(hed == fl->unionsym)
 			return cvalue_union_size(type, palign);
-		if(hed == pointersym || hed == cfunctionsym){
+		if(hed == fl->pointersym || hed == fl->cfunctionsym){
 			*palign = offsetof(struct{ char a; void *i; }, i);
 			return sizeof(void*);
 		}
-		if(hed == arraysym){
+		if(hed == fl->arraysym){
 			value_t t = car(cdr_(type));
 			if(!iscons(cdr_(cdr_(type))))
-				lerrorf(ArgError, "incomplete type");
+				lerrorf(fl->ArgError, "incomplete type");
 			value_t n = car_(cdr_(cdr_(type)));
 			size_t sz = toulong(n);
 			return sz * ctype_sizeof(t, palign);
 		}
-		if(hed == enumsym){
+		if(hed == fl->enumsym){
 			*palign = offsetof(struct{ char c; numerictype_t e; }, e);
 			return sizeof(numerictype_t);
 		}
 	}
 
-	lerrorf(ArgError, "invalid c type");
+	lerrorf(fl->ArgError, "invalid c type");
 }
 
 // get pointer and size for any plain-old-data value
@@ -673,7 +645,7 @@
 	if(iscvalue(v)){
 		cvalue_t *pcv = ptr(v);
 		ios_t *x = value2c(ios_t*, v);
-		if(cv_class(pcv) == iostreamtype && x->bm == bm_mem){
+		if(cv_class(pcv) == fl->iostreamtype && x->bm == bm_mem){
 			*pdata = x->buf;
 			*psz = x->size;
 			return;
@@ -709,20 +681,20 @@
 {
 	argcount(nargs, 1);
 	switch(tag(args[0])){
-	case TAG_CONS: return pairsym;
-	case TAG_NUM1: case TAG_NUM: return fixnumsym;
-	case TAG_SYM: return symbolsym;
-	case TAG_VECTOR: return vectorsym;
+	case TAG_CONS: return fl->pairsym;
+	case TAG_NUM1: case TAG_NUM: return fl->fixnumsym;
+	case TAG_SYM: return fl->symbolsym;
+	case TAG_VECTOR: return fl->vectorsym;
 	case TAG_FUNCTION:
-		if(args[0] == FL_T || args[0] == FL_F)
-			return booleansym;
-		if(args[0] == NIL)
-			return nullsym;
-		if(args[0] == FL_EOF)
+		if(args[0] == fl->FL_T || args[0] == fl->FL_F)
+			return fl->booleansym;
+		if(args[0] == fl->NIL)
+			return fl->nullsym;
+		if(args[0] == fl->FL_EOF)
 			return symbol("eof-object");
 		if(isbuiltin(args[0]))
-			return builtinsym;
-		return FUNCTION;
+			return fl->builtinsym;
+		return fl->FUNCTION;
 	}
 	return cv_type(ptr(args[0]));
 }
@@ -745,7 +717,7 @@
 	if(t->vtable != nil && t->vtable->relocate != nil)
 		t->vtable->relocate(v, ncv);
 	forward(v, ncv);
-	if(fl_exiting)
+	if(fl->exiting)
 		cv_autorelease(ptr(ncv));
 	return ncv;
 }
@@ -770,7 +742,7 @@
 		autorelease(ncv);
 		if(hasparent(cv)){
 			ncv->type = (fltype_t*)(((uintptr_t)ncv->type) & ~CV_PARENT_BIT);
-			ncv->parent = NIL;
+			ncv->parent = fl->NIL;
 		}
 	}else{
 		ncv->data = &ncv->_space[0];
@@ -783,11 +755,11 @@
 {
 	argcount(nargs, 1);
 	if(iscons(args[0]) || isvector(args[0]))
-		lerrorf(ArgError, "argument must be a leaf atom");
+		lerrorf(fl->ArgError, "argument must be a leaf atom");
 	if(!iscvalue(args[0]))
 		return args[0];
 	if(!cv_isPOD(ptr(args[0])))
-		lerrorf(ArgError, "argument must be a plain-old-data type");
+		lerrorf(fl->ArgError, "argument must be a plain-old-data type");
 	return cvalue_copy(args[0]);
 }
 
@@ -796,7 +768,7 @@
 	argcount(nargs, 1);
 	return (iscprim(args[0]) ||
 			(iscvalue(args[0]) && cv_isPOD((cvalue_t*)ptr(args[0])))) ?
-		FL_T : FL_F;
+		fl->FL_T : fl->FL_F;
 }
 
 static void
@@ -804,7 +776,7 @@
 {
 	cvinitfunc_t f = type->init;
 	if(f == nil)
-		lerrorf(ArgError, "invalid c type");
+		lerrorf(fl->ArgError, "invalid c type");
 	f(type, v, dest);
 }
 
@@ -925,7 +897,7 @@
 	symbol_t *name = tosymbol(args[0]);
 	cvalue_t *cv;
 	if(ismanaged(args[0]) || (cv = name->dlcache) == nil)
-		lerrorf(ArgError, "function %s not found", name->name);
+		lerrorf(fl->ArgError, "function %s not found", name->name);
 	return tagptr(cv, TAG_CVALUE);
 }
 
@@ -934,7 +906,7 @@
 {
 	cvalue_t *cv;
 	cv = calloc(CVALUE_NWORDS, sizeof(*cv));
-	cv->type = builtintype;
+	cv->type = fl->builtintype;
 	cv->data = &cv->_space[0];
 	cv->len = sizeof(value_t);
 	*(builtin_t*)cv->data = f;
@@ -941,7 +913,7 @@
 
 	value_t sym = symbol(name);
 	((symbol_t*)ptr(sym))->dlcache = cv;
-	ptrhash_put(&reverse_dlsym_lookup_table, cv, (void*)sym);
+	ptrhash_put(&fl->reverse_dlsym_lookup_table, cv, (void*)sym);
 
 	return tagptr(cv, TAG_CVALUE);
 }
@@ -948,7 +920,7 @@
 
 #define cv_intern(tok) \
 	do{ \
-		tok##sym = symbol(#tok); \
+		fl->tok##sym = symbol(#tok); \
 	}while(0)
 
 #define ctor_cv_intern(tok, nt, ctype) \
@@ -955,9 +927,9 @@
 	do{ \
 		symbol_t *s; \
 		cv_intern(tok); \
-		set(tok##sym, cbuiltin(#tok, fn_builtin_##tok)); \
+		set(fl->tok##sym, cbuiltin(#tok, fn_builtin_##tok)); \
 		if(valid_numtype(nt)){ \
-			s = ptr(tok##sym); \
+			s = ptr(fl->tok##sym); \
 			s->numtype = nt; \
 			s->size = sizeof(ctype); \
 			s->align = offsetof(struct{char c; ctype x;}, x); \
@@ -966,8 +938,8 @@
 
 #define mk_primtype(name, ctype) \
 	do{ \
-		name##type = get_type(name##sym); \
-		name##type->init = cvalue_##ctype##_init; \
+		fl->name##type = get_type(fl->name##sym); \
+		fl->name##type->init = cvalue_##ctype##_init; \
 	}while(0)
 
 #define RETURN_NUM_AS(var, type) return(mk_##type(var))
@@ -1333,7 +1305,7 @@
 _Noreturn void
 DivideByZeroError(void)
 {
-	lerrorf(DivideError, "/: division by zero");
+	lerrorf(fl->DivideError, "/: division by zero");
 }
 
 value_t
@@ -1500,7 +1472,7 @@
 	}
 	}
 	assert(0);
-	return NIL;
+	return fl->NIL;
 }
 
 BUILTIN("logand", logand)
@@ -1638,10 +1610,10 @@
 void
 cvalues_init(void)
 {
-	htable_new(&TypeTable, 256);
-	htable_new(&reverse_dlsym_lookup_table, 256);
+	htable_new(&fl->TypeTable, 256);
+	htable_new(&fl->reverse_dlsym_lookup_table, 256);
 
-	builtintype = define_opaque_type(builtinsym, sizeof(builtin_t), nil, nil);
+	fl->builtintype = define_opaque_type(fl->builtinsym, sizeof(builtin_t), nil, nil);
 
 	ctor_cv_intern(int8, T_INT8, int8_t);
 	ctor_cv_intern(uint8, T_UINT8, uint8_t);
@@ -1669,13 +1641,13 @@
 	cv_intern(struct);
 	cv_intern(union);
 	cv_intern(void);
-	cfunctionsym = symbol("c-function");
+	fl->cfunctionsym = symbol("c-function");
 
-	stringtypesym = symbol("*string-type*");
-	setc(stringtypesym, fl_list2(arraysym, bytesym));
+	fl->stringtypesym = symbol("*string-type*");
+	setc(fl->stringtypesym, fl_list2(fl->arraysym, fl->bytesym));
 
-	runestringtypesym = symbol("*runestring-type*");
-	setc(runestringtypesym, fl_list2(arraysym, runesym));
+	fl->runestringtypesym = symbol("*runestring-type*");
+	setc(fl->runestringtypesym, fl_list2(fl->arraysym, fl->runesym));
 
 	mk_primtype(int8, int8_t);
 	mk_primtype(uint8, uint8_t);
@@ -1698,13 +1670,13 @@
 	mk_primtype(double, double);
 
 	ctor_cv_intern(bignum, T_MPINT, mpint*);
-	mpinttype = get_type(bignumsym);
-	mpinttype->init = cvalue_mpint_init;
-	mpinttype->vtable = &mpint_vtable;
+	fl->mpinttype = get_type(fl->bignumsym);
+	fl->mpinttype->init = cvalue_mpint_init;
+	fl->mpinttype->vtable = &mpint_vtable;
 
-	stringtype = get_type(symbol_value(stringtypesym));
-	runestringtype = get_type(symbol_value(runestringtypesym));
+	fl->stringtype = get_type(symbol_value(fl->stringtypesym));
+	fl->runestringtype = get_type(symbol_value(fl->runestringtypesym));
 
-	emptystringsym = symbol("*empty-string*");
-	setc(emptystringsym, cvalue_static_cstring(""));
+	fl->emptystringsym = symbol("*empty-string*");
+	setc(fl->emptystringsym, cvalue_static_cstring(""));
 }
--- a/cvalues.h
+++ b/cvalues.h
@@ -14,21 +14,6 @@
 #define hasparent(cv) ((uintptr_t)(cv)->type & CV_PARENT_BIT)
 #define isinlined(cv) ((cv)->data == &(cv)->_space[0])
 
-extern value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
-extern value_t int64sym, uint64sym, bignumsym;
-extern value_t longsym, ulongsym, bytesym, runesym;
-extern value_t structsym, arraysym, enumsym, cfunctionsym, voidsym, pointersym;
-extern value_t stringtypesym, runestringtypesym, emptystringsym;
-extern value_t unionsym, floatsym, doublesym;
-
-extern fltype_t *bytetype, *runetype;
-extern fltype_t *stringtype, *runestringtype;
-extern fltype_t *builtintype;
-
-extern htable_t TypeTable;
-extern htable_t reverse_dlsym_lookup_table;
-extern fltype_t *mpinttype;
-
 void add_finalizer(cvalue_t *cv);
 void sweep_finalizers(void);
 void cv_autorelease(cvalue_t *cv);
--- a/equal.c
+++ b/equal.c
@@ -23,7 +23,7 @@
 {
 	value_t c = (value_t)ptrhash_get(table, (void*)key);
 	if(c == (value_t)HT_NOTFOUND)
-		return NIL;
+		return fl->NIL;
 	if(c == key)
 		return c;
 	return eq_class(table, c);
@@ -32,8 +32,8 @@
 static void
 eq_union(htable_t *table, value_t a, value_t b, value_t c, value_t cb)
 {
-	value_t ca = c == NIL ? a : c;
-	if(cb != NIL)
+	value_t ca = c == fl->NIL ? a : c;
+	if(cb != fl->NIL)
 		ptrhash_put(table, (void*)cb, (void*)ca);
 	ptrhash_put(table, (void*)a, (void*)ca);
 	ptrhash_put(table, (void*)b, (void*)ca);
@@ -53,7 +53,7 @@
 	m = la < lb ? la : lb;
 	for(i = 0; i < m; i++){
 		value_t d = bounded_compare(vector_elt(a, i), vector_elt(b, i), bound-1, eq);
-		if(d == NIL || numval(d) != 0)
+		if(d == fl->NIL || numval(d) != 0)
 			return d;
 	}
 	if(la < lb)
@@ -75,7 +75,7 @@
 	if(a == b)
 		return fixnum(0);
 	if(bound <= 0)
-		return NIL;
+		return fl->NIL;
 	int taga = tag(a);
 	int tagb = cmptag(b);
 	int c;
@@ -85,7 +85,7 @@
 		if(isfixnum(b))
 			return (numval(a) < numval(b)) ? fixnum(-1) : fixnum(1);
 		if(iscprim(b)){
-			if(cp_class((cprim_t*)ptr(b)) == runetype)
+			if(cp_class((cprim_t*)ptr(b)) == fl->runetype)
 				return fixnum(1);
 			return fixnum(numeric_compare(a, b, eq, 1, 0));
 		}
@@ -106,10 +106,10 @@
 			return bounded_vector_compare(a, b, bound, eq);
 		break;
 	case TAG_CPRIM:
-		if(cp_class((cprim_t*)ptr(a)) == runetype){
-			if(!iscprim(b) || cp_class(ptr(b)) != runetype)
+		if(cp_class((cprim_t*)ptr(a)) == fl->runetype){
+			if(!iscprim(b) || cp_class(ptr(b)) != fl->runetype)
 				return fixnum(-1);
-		}else if(iscprim(b) && cp_class(ptr(b)) == runetype)
+		}else if(iscprim(b) && cp_class(ptr(b)) == fl->runetype)
 			return fixnum(1);
 		c = numeric_compare(a, b, eq, 1, 0);
 		if(c != 2)
@@ -133,13 +133,13 @@
 				function_t *fa = ptr(a);
 				function_t *fb = ptr(b);
 				d = bounded_compare(fa->bcode, fb->bcode, bound-1, eq);
-				if(d == NIL || numval(d) != 0)
+				if(d == fl->NIL || numval(d) != 0)
 					return d;
 				d = bounded_compare(fa->vals, fb->vals, bound-1, eq);
-				if(d == NIL || numval(d) != 0)
+				if(d == fl->NIL || numval(d) != 0)
 					return d;
 				d = bounded_compare(fa->env, fb->env, bound-1, eq);
-				if(d == NIL || numval(d) != 0)
+				if(d == fl->NIL || numval(d) != 0)
 					return d;
 				return fixnum(0);
 			}
@@ -150,7 +150,7 @@
 		if(tagb < TAG_CONS)
 			return fixnum(1);
 		d = bounded_compare(car_(a), car_(b), bound-1, eq);
-		if(d == NIL || numval(d) != 0)
+		if(d == fl->NIL || numval(d) != 0)
 			return d;
 		a = cdr_(a); b = cdr_(b);
 		bound--;
@@ -176,7 +176,7 @@
 		xb = vector_elt(b, i);
 		if(leafp(xa) || leafp(xb)){
 			d = bounded_compare(xa, xb, 1, eq);
-			if(d != NIL && numval(d) != 0)
+			if(d != fl->NIL && numval(d) != 0)
 				return d;
 		}else if(tag(xa) < tag(xb))
 			return fixnum(-1);
@@ -186,7 +186,7 @@
 
 	ca = eq_class(table, a);
 	cb = eq_class(table, b);
-	if(ca != NIL && ca == cb)
+	if(ca != fl->NIL && ca == cb)
 		return fixnum(0);
 
 	eq_union(table, a, b, ca, cb);
@@ -227,7 +227,7 @@
 			int tagdb = tag(db);
 			if(leafp(aa) || leafp(ab)){
 				d = bounded_compare(aa, ab, 1, eq);
-				if(d != NIL && numval(d) != 0)
+				if(d != fl->NIL && numval(d) != 0)
 					return d;
 			}
 			if(tagaa < tagab)
@@ -236,7 +236,7 @@
 				return fixnum(1);
 			if(leafp(da) || leafp(db)){
 				d = bounded_compare(da, db, 1, eq);
-				if(d != NIL && numval(d) != 0)
+				if(d != fl->NIL && numval(d) != 0)
 					return d;
 			}
 			if(tagda < tagdb)
@@ -246,7 +246,7 @@
 
 			ca = eq_class(table, a);
 			cb = eq_class(table, b);
-			if(ca != NIL && ca == cb)
+			if(ca != fl->NIL && ca == cb)
 				return fixnum(0);
 
 			eq_union(table, a, b, ca, cb);
@@ -271,7 +271,7 @@
 
 		ca = eq_class(table, a);
 		cb = eq_class(table, b);
-		if(ca != NIL && ca == cb)
+		if(ca != fl->NIL && ca == cb)
 			return fixnum(0);
 
 		eq_union(table, a, b, ca, cb);
@@ -298,7 +298,7 @@
 compare_(value_t a, value_t b, int eq)
 {
 	value_t guess = bounded_compare(a, b, BOUNDED_COMPARE_BOUND, eq);
-	if(guess == NIL){
+	if(guess == fl->NIL){
 		guess = cyc_compare(a, b, &equal_eq_hashtable, eq);
 		htable_reset(&equal_eq_hashtable, 512);
 	}
@@ -315,8 +315,8 @@
 fl_equal(value_t a, value_t b)
 {
 	if(eq_comparable(a, b))
-		return a == b ? FL_T : FL_F;
-	return numval(compare_(a, b, 1)) == 0 ? FL_T : FL_F;
+		return a == b ? fl->FL_T : fl->FL_F;
+	return numval(compare_(a, b, 1)) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 /*
@@ -366,7 +366,7 @@
 	case TAG_CPRIM:
 		cp = ptr(a);
 		data = cp_data(cp);
-		if(cp_class(cp) == runetype)
+		if(cp_class(cp) == fl->runetype)
 			return inthash(*(Rune*)data);
 		nt = cp_numtype(cp);
 		u.d = conv_to_double(data, nt);
@@ -374,7 +374,7 @@
 	case TAG_CVALUE:
 		cv = (cvalue_t*)ptr(a);
 		data = cv_data(cv);
-		if(cv->type == mpinttype){
+		if(cv->type == fl->mpinttype){
 			len = mptobe(*(mpint**)data, nil, 0, (uint8_t**)&data);
 			h = memhash(data, len);
 			LLT_FREE(data);
--- a/flisp.c
+++ b/flisp.c
@@ -25,39 +25,8 @@
         builtin_t fptr;
 }builtinspec_t;
 
-#define N_GC_HANDLES 1024
-static value_t *GCHandleStack[N_GC_HANDLES];
-static uint32_t N_GCHND = 0;
+__thread Fl *fl;
 
-value_t *Stack;
-uint32_t SP = 0;
-static uint32_t N_STACK;
-static uint32_t curr_frame = 0;
-
-value_t FL_NIL, FL_T, FL_F, FL_EOF, QUOTE;
-value_t NIL, LAMBDA, IF, TRYCATCH;
-value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT, FUNCTION;
-
-value_t printwidthsym, printreadablysym, printprettysym, printlengthsym;
-value_t printlevelsym, builtins_table_sym;
-value_t pairsym, symbolsym, fixnumsym, vectorsym, builtinsym, vu8sym;
-value_t definesym, defmacrosym, forsym, setqsym;
-value_t tsym, Tsym, fsym, Fsym, booleansym, nullsym, evalsym, fnsym;
-// for reading characters
-value_t nulsym, alarmsym, backspacesym, tabsym, linefeedsym, newlinesym;
-value_t vtabsym, pagesym, returnsym, escsym, spacesym, deletesym;
-
-value_t IOError, ParseError, TypeError, ArgError, MemoryError;
-value_t DivideError, BoundsError, Error, KeyError, EnumerationError;
-static value_t UnboundError;
-
-uint8_t *fromspace;
-uint8_t *tospace;
-uint8_t *curheap;
-uint8_t *lim;
-uint32_t heapsize;//bytes
-uint32_t *consflags;
-
 int
 isbuiltin(value_t x)
 {
@@ -69,8 +38,6 @@
 
 // error utilities ------------------------------------------------------------
 
-fl_readstate_t *readstate = nil;
-
 void
 free_readstate(fl_readstate_t *rs)
 {
@@ -77,17 +44,11 @@
 	htable_free(&rs->backrefs);
 	htable_free(&rs->gensyms);
 }
-// saved execution state for an unwind target
-fl_exception_context_t *fl_ctx = nil;
-uint32_t fl_throwing_frame = 0;  // active frame when exception was thrown
-value_t fl_lasterror;
 
-bool fl_exiting = false;
-
 _Noreturn void
 fl_exit(int status)
 {
-	fl_exiting = true;
+	fl->exiting = true;
 	gc(0);
 	exit(status);
 }
@@ -94,13 +55,13 @@
 
 #define FL_TRY \
 	fl_exception_context_t _ctx; int l__tr, l__ca; \
-	_ctx.sp = SP; _ctx.frame = curr_frame; _ctx.rdst = readstate; _ctx.prev = fl_ctx; \
-	_ctx.ngchnd = N_GCHND; fl_ctx = &_ctx; \
+	_ctx.sp = fl->SP; _ctx.frame = fl->curr_frame; _ctx.rdst = fl->readstate; _ctx.prev = fl->exctx; \
+	_ctx.ngchnd = fl->N_GCHND; fl->exctx = &_ctx; \
 	if(!setjmp(_ctx.buf)) \
-		for(l__tr = 1; l__tr; l__tr = 0, (void)(fl_ctx = fl_ctx->prev))
+		for(l__tr = 1; l__tr; l__tr = 0, (void)(fl->exctx = fl->exctx->prev))
 
 #define FL_CATCH_INC \
-	l__ca = 0, fl_lasterror = FL_NIL, fl_throwing_frame = 0, SP = _ctx.sp, curr_frame = _ctx.frame
+	l__ca = 0, fl->lasterror = fl->FL_NIL, fl->throwing_frame = 0, fl->SP = _ctx.sp, fl->curr_frame = _ctx.frame
 
 #define FL_CATCH \
 	else \
@@ -113,37 +74,37 @@
 void
 fl_savestate(fl_exception_context_t *_ctx)
 {
-	_ctx->sp = SP;
-	_ctx->frame = curr_frame;
-	_ctx->rdst = readstate;
-	_ctx->prev = fl_ctx;
-	_ctx->ngchnd = N_GCHND;
+	_ctx->sp = fl->SP;
+	_ctx->frame = fl->curr_frame;
+	_ctx->rdst = fl->readstate;
+	_ctx->prev = fl->exctx;
+	_ctx->ngchnd = fl->N_GCHND;
 }
 
 void
 fl_restorestate(fl_exception_context_t *_ctx)
 {
-	fl_lasterror = FL_NIL;
-	fl_throwing_frame = 0;
-	SP = _ctx->sp;
-	curr_frame = _ctx->frame;
+	fl->lasterror = fl->FL_NIL;
+	fl->throwing_frame = 0;
+	fl->SP = _ctx->sp;
+	fl->curr_frame = _ctx->frame;
 }
 
 _Noreturn void
 fl_raise(value_t e)
 {
-	fl_lasterror = e;
+	fl->lasterror = e;
 	// unwind read state
-	while(readstate != fl_ctx->rdst){
-		free_readstate(readstate);
-		readstate = readstate->prev;
+	while(fl->readstate != fl->exctx->rdst){
+		free_readstate(fl->readstate);
+		fl->readstate = fl->readstate->prev;
 	}
-	if(fl_throwing_frame == 0)
-		fl_throwing_frame = curr_frame;
-	N_GCHND = fl_ctx->ngchnd;
-	fl_exception_context_t *thisctx = fl_ctx;
-	if(fl_ctx->prev)   // don't throw past toplevel
-		fl_ctx = fl_ctx->prev;
+	if(fl->throwing_frame == 0)
+		fl->throwing_frame = fl->curr_frame;
+	fl->N_GCHND = fl->exctx->ngchnd;
+	fl_exception_context_t *thisctx = fl->exctx;
+	if(fl->exctx->prev)   // don't throw past toplevel
+		fl->exctx = fl->exctx->prev;
 	longjmp(thisctx->buf, 1);
 }
 
@@ -166,19 +127,19 @@
 _Noreturn void
 type_error(char *expected, value_t got)
 {
-	fl_raise(fl_listn(3, TypeError, symbol(expected), got));
+	fl_raise(fl_listn(3, fl->TypeError, symbol(expected), got));
 }
 
 _Noreturn void
 bounds_error(value_t arr, value_t ind)
 {
-	fl_raise(fl_listn(3, BoundsError, arr, ind));
+	fl_raise(fl_listn(3, fl->BoundsError, arr, ind));
 }
 
 _Noreturn void
 unbound_error(value_t sym)
 {
-	fl_raise(fl_listn(2, UnboundError, sym));
+	fl_raise(fl_listn(2, fl->UnboundError, sym));
 }
 
 // safe cast operators --------------------------------------------------------
@@ -200,8 +161,6 @@
 
 // symbol table ---------------------------------------------------------------
 
-symbol_t *symtab = nil;
-
 int
 fl_is_keyword_name(char *str, size_t len)
 {
@@ -243,23 +202,18 @@
 {
 	symbol_t **pnode;
 
-	pnode = symtab_lookup(&symtab, str);
+	pnode = symtab_lookup(&fl->symtab, str);
 	if(*pnode == nil)
 		*pnode = mk_symbol(str);
 	return tagptr(*pnode, TAG_SYM);
 }
 
-static uint32_t _gensym_ctr = 0;
-// two static buffers for gensym printing so there can be two
-// gensym names available at a time, mostly for compare()
-static char gsname[2][16];
-static int gsnameno = 0;
 BUILTIN("gensym", gensym)
 {
 	argcount(nargs, 0);
 	USED(args);
 	gensym_t *gs = alloc_words(sizeof(gensym_t)/sizeof(void*));
-	gs->id = _gensym_ctr++;
+	gs->id = fl->_gensym_ctr++;
 	gs->binding = UNBOUND;
 	gs->isconst = 0;
 	gs->type = nil;
@@ -275,7 +229,7 @@
 BUILTIN("gensym?", gensymp)
 {
 	argcount(nargs, 1);
-	return isgensym(args[0]) ? FL_T : FL_F;
+	return isgensym(args[0]) ? fl->FL_T : fl->FL_F;
 }
 
 char *
@@ -304,8 +258,8 @@
 {
 	if(ismanaged(v)){
 		gensym_t *gs = (gensym_t*)ptr(v);
-		gsnameno = 1-gsnameno;
-		char *n = uint2str(gsname[gsnameno]+1, sizeof(gsname[0])-1, gs->id, 10);
+		fl->gsnameno = 1-fl->gsnameno;
+		char *n = uint2str(fl->gsname[fl->gsnameno]+1, sizeof(fl->gsname[0])-1, gs->id, 10);
 		*(--n) = 'g';
 		return n;
 	}
@@ -319,10 +273,10 @@
 {
 	cons_t *c;
 
-	if(__unlikely(curheap > lim))
+	if(__unlikely(fl->curheap > fl->lim))
 		gc(0);
-	c = (cons_t*)curheap;
-	curheap += sizeof(cons_t);
+	c = (cons_t*)fl->curheap;
+	fl->curheap += sizeof(cons_t);
 	return tagptr(c, TAG_CONS);
 }
 
@@ -333,24 +287,21 @@
 
 	assert(n > 0);
 	n = LLT_ALIGN(n, 2);   // only allocate multiples of 2 words
-	if(__unlikely((value_t*)curheap > ((value_t*)lim)+2-n)){
+	if(__unlikely((value_t*)fl->curheap > ((value_t*)fl->lim)+2-n)){
 		gc(0);
-		while((value_t*)curheap > ((value_t*)lim)+2-n){
+		while((value_t*)fl->curheap > ((value_t*)fl->lim)+2-n)
 			gc(1);
-		}
 	}
-	first = (value_t*)curheap;
-	curheap += (n*sizeof(value_t));
+	first = (value_t*)fl->curheap;
+	fl->curheap += (n*sizeof(value_t));
 	return first;
 }
 
-value_t the_empty_vector;
-
 value_t
 alloc_vector(size_t n, int init)
 {
 	if(n == 0)
-		return the_empty_vector;
+		return fl->the_empty_vector;
 	value_t *c = alloc_words(n+1);
 	value_t v = tagptr(c, TAG_VECTOR);
 	vector_setsize(v, n);
@@ -357,7 +308,7 @@
 	if(init){
 		unsigned int i;
 		for(i = 0; i < n; i++)
-			vector_elt(v, i) = FL_UNSPECIFIED;
+			vector_elt(v, i) = fl->FL_UNSPECIFIED;
 	}
 	return v;
 }
@@ -367,16 +318,16 @@
 void
 fl_gc_handle(value_t *pv)
 {
-	if(N_GCHND >= N_GC_HANDLES)
-		lerrorf(MemoryError, "out of gc handles");
-	GCHandleStack[N_GCHND++] = pv;
+	if(fl->N_GCHND >= N_GC_HANDLES)
+		lerrorf(fl->MemoryError, "out of gc handles");
+	fl->GCHandleStack[fl->N_GCHND++] = pv;
 }
 
 void
 fl_free_gc_handles(uint32_t n)
 {
-	assert(N_GCHND >= n);
-	N_GCHND -= n;
+	assert(fl->N_GCHND >= n);
+	fl->N_GCHND -= n;
 }
 
 value_t
@@ -393,8 +344,8 @@
 				*pcdr = cdr_(v);
 				return first;
 			}
-			*pcdr = nc = tagptr((cons_t*)curheap, TAG_CONS);
-			curheap += sizeof(cons_t);
+			*pcdr = nc = tagptr((cons_t*)fl->curheap, TAG_CONS);
+			fl->curheap += sizeof(cons_t);
 			d = cdr_(v);
 			car_(v) = TAG_FWD;
 			cdr_(v) = nc;
@@ -402,7 +353,7 @@
 			pcdr = &cdr_(nc);
 			v = d;
 		}while(iscons(v));
-		*pcdr = d == NIL ? NIL : relocate(d);
+		*pcdr = d == fl->NIL ? fl->NIL : relocate(d);
 		return first;
 	}
 
@@ -491,42 +442,39 @@
 	}
 }
 
-static value_t memory_exception_value;
-
 void
 gc(int mustgrow)
 {
-	static int grew = 0;
 	void *temp;
 	uint32_t i, f, top;
 	fl_readstate_t *rs;
 
-	curheap = tospace;
-	if(grew)
-		lim = curheap+heapsize*2-sizeof(cons_t);
+	fl->curheap = fl->tospace;
+	if(fl->grew)
+		fl->lim = fl->curheap+fl->heapsize*2-sizeof(cons_t);
 	else
-		lim = curheap+heapsize-sizeof(cons_t);
+		fl->lim = fl->curheap+fl->heapsize-sizeof(cons_t);
 
-	if(fl_throwing_frame > curr_frame){
-		top = fl_throwing_frame - 4;
-		f = Stack[fl_throwing_frame-4];
+	if(fl->throwing_frame > fl->curr_frame){
+		top = fl->throwing_frame - 4;
+		f = fl->Stack[fl->throwing_frame-4];
 	}else{
-		top = SP;
-		f = curr_frame;
+		top = fl->SP;
+		f = fl->curr_frame;
 	}
 	while(1){
 		for(i = f; i < top; i++)
-			Stack[i] = relocate(Stack[i]);
+			fl->Stack[i] = relocate(fl->Stack[i]);
 		if(f == 0)
 			break;
 		top = f - 4;
-		f = Stack[f-4];
+		f = fl->Stack[f-4];
 	}
-	for(i = 0; i < N_GCHND; i++)
-		*GCHandleStack[i] = relocate(*GCHandleStack[i]);
-	trace_globals(symtab);
+	for(i = 0; i < fl->N_GCHND; i++)
+		*fl->GCHandleStack[i] = relocate(*fl->GCHandleStack[i]);
+	trace_globals(fl->symtab);
 	relocate_typetable();
-	rs = readstate;
+	rs = fl->readstate;
 	while(rs){
 		value_t ent;
 		for(i = 0; i < rs->backrefs.size; i++){
@@ -542,38 +490,38 @@
 		rs->source = relocate(rs->source);
 		rs = rs->prev;
 	}
-	fl_lasterror = relocate(fl_lasterror);
-	memory_exception_value = relocate(memory_exception_value);
-	the_empty_vector = relocate(the_empty_vector);
+	fl->lasterror = relocate(fl->lasterror);
+	fl->memory_exception_value = relocate(fl->memory_exception_value);
+	fl->the_empty_vector = relocate(fl->the_empty_vector);
 
 	sweep_finalizers();
 
 #ifdef VERBOSEGC
 	printf("GC: found %d/%d live conses\n",
-		   (curheap-tospace)/sizeof(cons_t), heapsize/sizeof(cons_t));
+		   (fl->curheap-fl->tospace)/sizeof(cons_t), fl->heapsize/sizeof(cons_t));
 #endif
-	temp = tospace;
-	tospace = fromspace;
-	fromspace = temp;
+	temp = fl->tospace;
+	fl->tospace = fl->fromspace;
+	fl->fromspace = temp;
 
 	// if we're using > 80% of the space, resize tospace so we have
 	// more space to fill next time. if we grew tospace last time,
 	// grow the other half of the heap this time to catch up.
-	if(grew || ((lim-curheap) < (int)(heapsize/5)) || mustgrow){
-		temp = LLT_REALLOC(tospace, heapsize*2);
+	if(fl->grew || ((fl->lim-fl->curheap) < (int)(fl->heapsize/5)) || mustgrow){
+		temp = LLT_REALLOC(fl->tospace, fl->heapsize*2);
 		if(temp == nil)
-			fl_raise(memory_exception_value);
-		tospace = temp;
-		if(grew){
-			heapsize *= 2;
-			temp = bitvector_resize(consflags, 0, heapsize/sizeof(cons_t), 1);
+			fl_raise(fl->memory_exception_value);
+		fl->tospace = temp;
+		if(fl->grew){
+			fl->heapsize *= 2;
+			temp = bitvector_resize(fl->consflags, 0, fl->heapsize/sizeof(cons_t), 1);
 			if(temp == nil)
-				fl_raise(memory_exception_value);
-			consflags = (uint32_t*)temp;
+				fl_raise(fl->memory_exception_value);
+			fl->consflags = (uint32_t*)temp;
 		}
-		grew = !grew;
+		fl->grew = !fl->grew;
 	}
-	if(curheap > lim)  // all data was live
+	if(fl->curheap > fl->lim)  // all data was live
 		gc(0);
 }
 
@@ -580,12 +528,12 @@
 static void
 grow_stack(void)
 {
-	size_t newsz = N_STACK * 2;
-	value_t *ns = LLT_REALLOC(Stack, newsz*sizeof(value_t));
+	size_t newsz = fl->N_STACK * 2;
+	value_t *ns = LLT_REALLOC(fl->Stack, newsz*sizeof(value_t));
 	if(ns == nil)
-		lerrorf(MemoryError, "stack overflow");
-	Stack = ns;
-	N_STACK = newsz;
+		lerrorf(fl->MemoryError, "stack overflow");
+	fl->Stack = ns;
+	fl->N_STACK = newsz;
 }
 
 // utils ----------------------------------------------------------------------
@@ -594,23 +542,23 @@
 static value_t
 _applyn(uint32_t n)
 {
-	value_t f = Stack[SP-n-1];
-	uint32_t saveSP = SP;
+	value_t f = fl->Stack[fl->SP-n-1];
+	uint32_t saveSP = fl->SP;
 	value_t v;
 	if(iscbuiltin(f)){
-		v = ((builtin_t*)ptr(f))[3](&Stack[SP-n], n);
+		v = ((builtin_t*)ptr(f))[3](&fl->Stack[fl->SP-n], n);
 	}else if(isfunction(f)){
 		v = apply_cl(n);
 	}else if(isbuiltin(f)){
-		value_t tab = symbol_value(builtins_table_sym);
+		value_t tab = symbol_value(fl->builtins_table_sym);
 		if(ptr(tab) == nil)
 			unbound_error(tab);
-		Stack[SP-n-1] = vector_elt(tab, uintval(f));
+		fl->Stack[fl->SP-n-1] = vector_elt(tab, uintval(f));
 		v = apply_cl(n);
 	}else{
 		type_error("function", f);
 	}
-	SP = saveSP;
+	fl->SP = saveSP;
 	return v;
 }
 
@@ -618,16 +566,16 @@
 fl_apply(value_t f, value_t l)
 {
 	value_t v = l;
-	uint32_t n = SP;
+	uint32_t n = fl->SP;
 
 	PUSH(f);
 	while(iscons(v)){
-		if(SP >= N_STACK)
+		if(fl->SP >= fl->N_STACK)
 			grow_stack();
 		PUSH(car_(v));
 		v = cdr_(v);
 	}
-	n = SP - n - 1;
+	n = fl->SP - n - 1;
 	v = _applyn(n);
 	POPN(n+1);
 	return v;
@@ -641,7 +589,7 @@
 	size_t i;
 
 	PUSH(f);
-	while(SP+n > N_STACK)
+	while(fl->SP+n > fl->N_STACK)
 		grow_stack();
 	for(i = 0; i < n; i++){
 		value_t a = va_arg(ap, value_t);
@@ -658,10 +606,10 @@
 {
 	va_list ap;
 	va_start(ap, n);
-	uint32_t si = SP;
+	uint32_t si = fl->SP;
 	size_t i;
 
-	while(SP+n > N_STACK)
+	while(fl->SP+n > fl->N_STACK)
 		grow_stack();
 	for(i = 0; i < n; i++){
 		value_t a = va_arg(ap, value_t);
@@ -670,11 +618,11 @@
 	cons_t *c = alloc_words(n*2);
 	cons_t *l = c;
 	for(i = 0; i < n; i++){
-		c->car = Stack[si++];
+		c->car = fl->Stack[si++];
 		c->cdr = tagptr(c+1, TAG_CONS);
 		c++;
 	}
-	c[-1].cdr = NIL;
+	c[-1].cdr = fl->NIL;
 
 	POPN(n);
 	va_end(ap);
@@ -692,7 +640,7 @@
 	c[0].car = a;
 	c[0].cdr = tagptr(c+1, TAG_CONS);
 	c[1].car = b;
-	c[1].cdr = NIL;
+	c[1].cdr = fl->NIL;
 	return tagptr(c, TAG_CONS);
 }
 
@@ -714,7 +662,7 @@
 		return 1;
 	if(iscprim(v)){
 		cprim_t *c = ptr(v);
-		return c->type != runetype;
+		return c->type != fl->runetype;
 	}
 	if(iscvalue(v)){
 		cvalue_t *c = ptr(v);
@@ -741,7 +689,7 @@
 	if(star)
 		(c-2)->cdr = (c-1)->car;
 	else
-		(c-1)->cdr = NIL;
+		(c-1)->cdr = fl->NIL;
 	return v;
 }
 
@@ -749,21 +697,21 @@
 copy_list(value_t L)
 {
 	if(!iscons(L))
-		return NIL;
-	PUSH(NIL);
+		return fl->NIL;
+	PUSH(fl->NIL);
 	PUSH(L);
-	value_t *plcons = &Stack[SP-2];
-	value_t *pL = &Stack[SP-1];
+	value_t *plcons = &fl->Stack[fl->SP-2];
+	value_t *pL = &fl->Stack[fl->SP-1];
 	value_t c;
 	c = mk_cons(); PUSH(c);  // save first cons
 	car_(c) = car_(*pL);
-	cdr_(c) = NIL;
+	cdr_(c) = fl->NIL;
 	*plcons = c;
 	*pL = cdr_(*pL);
 	while(iscons(*pL)){
 		c = mk_cons();
 		car_(c) = car_(*pL);
-		cdr_(c) = NIL;
+		cdr_(c) = fl->NIL;
 		cdr_(*plcons) = c;
 		*plcons = c;
 		*pL = cdr_(*pL);
@@ -776,22 +724,22 @@
 static value_t
 do_trycatch(void)
 {
-	uint32_t saveSP = SP;
-	value_t v = NIL;
-	value_t thunk = Stack[SP-2];
-	Stack[SP-2] = Stack[SP-1];
-	Stack[SP-1] = thunk;
+	uint32_t saveSP = fl->SP;
+	value_t v = fl->NIL;
+	value_t thunk = fl->Stack[fl->SP-2];
+	fl->Stack[fl->SP-2] = fl->Stack[fl->SP-1];
+	fl->Stack[fl->SP-1] = thunk;
 
 	FL_TRY{
 		v = apply_cl(0);
 	}
 	FL_CATCH{
-		v = Stack[saveSP-2];
+		v = fl->Stack[saveSP-2];
 		PUSH(v);
-		PUSH(fl_lasterror);
+		PUSH(fl->lasterror);
 		v = apply_cl(1);
 	}
-	SP = saveSP;
+	fl->SP = saveSP;
 	return v;
 }
 
@@ -804,20 +752,20 @@
 {
 	uint32_t extr = nopt+nkw;
 	uint32_t ntot = nreq+extr;
-	value_t args[64], v = NIL;
+	value_t args[64], v = fl->NIL;
 	uint32_t i, a = 0, nrestargs;
-	value_t s1 = Stack[SP-1];
-	value_t s2 = Stack[SP-2];
-	value_t s4 = Stack[SP-4];
-	value_t s5 = Stack[SP-5];
+	value_t s1 = fl->Stack[fl->SP-1];
+	value_t s2 = fl->Stack[fl->SP-2];
+	value_t s4 = fl->Stack[fl->SP-4];
+	value_t s5 = fl->Stack[fl->SP-5];
 	if(nargs < nreq)
-		lerrorf(ArgError, "too few arguments");
+		lerrorf(fl->ArgError, "too few arguments");
 	if(extr > nelem(args))
-		lerrorf(ArgError, "too many arguments");
+		lerrorf(fl->ArgError, "too many arguments");
 	for(i = 0; i < extr; i++)
 		args[i] = UNBOUND;
 	for(i = nreq; i < nargs; i++){
-		v = Stack[bp+i];
+		v = fl->Stack[bp+i];
 		if(issymbol(v) && iskeyword((symbol_t*)ptr(v)))
 			break;
 		if(a >= nopt)
@@ -831,7 +779,7 @@
 	do{
 		i++;
 		if(i >= nargs)
-			lerrorf(ArgError, "keyword %s requires an argument", symbol_name(v));
+			lerrorf(fl->ArgError, "keyword %s requires an argument", symbol_name(v));
 		value_t hv = fixnum(((symbol_t*)ptr(v))->hash);
 		lltint_t lx = numval(hv);
 		uintptr_t x = 2*((lx < 0 ? -lx : lx) % n);
@@ -841,32 +789,32 @@
 			idx += nopt;
 			if(args[idx] == UNBOUND){
 				// if duplicate key, keep first value
-				args[idx] = Stack[bp+i];
+				args[idx] = fl->Stack[bp+i];
 			}
 		}else{
-			lerrorf(ArgError, "unsupported keyword %s", symbol_name(v));
+			lerrorf(fl->ArgError, "unsupported keyword %s", symbol_name(v));
 		}
 		i++;
 		if(i >= nargs)
 			break;
-		v = Stack[bp+i];
+		v = fl->Stack[bp+i];
 	}while(issymbol(v) && iskeyword((symbol_t*)ptr(v)));
 no_kw:
 	nrestargs = nargs - i;
 	if(!va && nrestargs > 0)
-		lerrorf(ArgError, "too many arguments");
+		lerrorf(fl->ArgError, "too many arguments");
 	nargs = ntot + nrestargs;
 	if(nrestargs)
-		memmove(&Stack[bp+ntot], &Stack[bp+i], nrestargs*sizeof(value_t));
-	memmove(&Stack[bp+nreq], args, extr*sizeof(value_t));
-	SP = bp + nargs;
-	assert(SP < N_STACK-5);
+		memmove(&fl->Stack[bp+ntot], &fl->Stack[bp+i], nrestargs*sizeof(value_t));
+	memmove(&fl->Stack[bp+nreq], args, extr*sizeof(value_t));
+	fl->SP = bp + nargs;
+	assert(fl->SP < fl->N_STACK-5);
 	PUSH(s5);
 	PUSH(s4);
 	PUSH(nargs);
 	PUSH(s2);
 	PUSH(s1);
-	curr_frame = SP;
+	fl->curr_frame = fl->SP;
 	return nargs;
 }
 
@@ -907,7 +855,7 @@
 static value_t
 apply_cl(uint32_t nargs)
 {
-	uint32_t top_frame = curr_frame;
+	uint32_t top_frame = fl->curr_frame;
 	// frame variables
 	uint32_t n, captured;
 	uint32_t bp;
@@ -929,40 +877,40 @@
 	USED(v);
 apply_cl_top:
 	captured = 0;
-	func = Stack[SP-nargs-1];
+	func = fl->Stack[fl->SP-nargs-1];
 	ip = cv_data((cvalue_t*)ptr(fn_bcode(func)));
 	assert(!ismanaged((uintptr_t)ip));
-	while(SP+GET_INT32(ip) > N_STACK)
+	while(fl->SP+GET_INT32(ip) > fl->N_STACK)
 		grow_stack();
 	ip += 4;
 
-	bp = SP-nargs;
+	bp = fl->SP-nargs;
 	PUSH(fn_env(func));
-	PUSH(curr_frame);
+	PUSH(fl->curr_frame);
 	PUSH(nargs);
-	SP++;//PUSH(0); //ip
+	fl->SP++;//PUSH(0); //ip
 	PUSH(0); //captured?
-	curr_frame = SP;
+	fl->curr_frame = fl->SP;
 
 	op = *ip++;
 	while(1){
 		switch(op){
 		OP(OP_LOADA0)
-			PUSH(captured ? vector_elt(Stack[bp], 0) : Stack[bp]);
+			PUSH(captured ? vector_elt(fl->Stack[bp], 0) : fl->Stack[bp]);
 			NEXT_OP;
 
 		OP(OP_LOADA1)
-			PUSH(captured ? vector_elt(Stack[bp], 1) : Stack[bp+1]);
+			PUSH(captured ? vector_elt(fl->Stack[bp], 1) : fl->Stack[bp+1]);
 			NEXT_OP;
 
 		OP(OP_LOADV)
-			v = fn_vals(Stack[bp-1]);
+			v = fn_vals(fl->Stack[bp-1]);
 			assert(*ip < vector_size(v));
 			PUSH(vector_elt(v, *ip++));
 			NEXT_OP;
 
 		OP(OP_BRF)
-			ip += POP() == FL_F ? GET_INT16(ip) : 2;
+			ip += POP() == fl->FL_F ? GET_INT16(ip) : 2;
 			NEXT_OP;
 
 		OP(OP_POP)
@@ -987,16 +935,16 @@
 				n = *ip++;  // nargs
 			}
 		do_call:
-			func = Stack[SP-n-1];
+			func = fl->Stack[fl->SP-n-1];
 			if(tag(func) == TAG_FUNCTION){
 				if(func > (N_BUILTINS<<3)){
 					if(tail){
-						curr_frame = Stack[curr_frame-4];
+						fl->curr_frame = fl->Stack[fl->curr_frame-4];
 						for(s = -1; s < (fixnum_t)n; s++)
-							Stack[bp+s] = Stack[SP-n+s];
-						SP = bp+n;
+							fl->Stack[bp+s] = fl->Stack[fl->SP-n+s];
+						fl->SP = bp+n;
 					}else{
-						Stack[curr_frame-2] = (uintptr_t)ip;
+						fl->Stack[fl->curr_frame-2] = (uintptr_t)ip;
 					}
 					nargs = n;
 					goto apply_cl_top;
@@ -1009,9 +957,9 @@
 						else if(s != ANYARGS && (signed)n < -s)
 							argcount(n, -s);
 						// remove function arg
-						for(s = SP-n-1; s < (int)SP-1; s++)
-							Stack[s] = Stack[s+1];
-						SP--;
+						for(s = fl->SP-n-1; s < (int)fl->SP-1; s++)
+							fl->Stack[s] = fl->Stack[s+1];
+						fl->SP--;
 						switch(i){
 						case OP_LIST:   goto apply_list;
 						case OP_VECTOR: goto apply_vector;
@@ -1027,21 +975,21 @@
 					}
 				}
 			}else if(iscbuiltin(func)){
-				s = SP;
-				v = (((builtin_t*)ptr(func))[3])(&Stack[SP-n], n);
-				SP = s-n;
-				Stack[SP-1] = v;
+				s = fl->SP;
+				v = (((builtin_t*)ptr(func))[3])(&fl->Stack[fl->SP-n], n);
+				fl->SP = s-n;
+				fl->Stack[fl->SP-1] = v;
 				NEXT_OP;
 			}
 			type_error("function", func);
 
 		OP(OP_LOADGL)
-			v = fn_vals(Stack[bp-1]);
+			v = fn_vals(fl->Stack[bp-1]);
 			v = vector_elt(v, GET_INT32(ip));
 			ip += 4;
 			if(0){
 		OP(OP_LOADG)
-				v = fn_vals(Stack[bp-1]);
+				v = fn_vals(fl->Stack[bp-1]);
 				assert(*ip < vector_size(v));
 				v = vector_elt(v, *ip);
 				ip++;
@@ -1057,12 +1005,12 @@
 			assert(nargs > 0);
 			i = *ip++;
 			if(captured){
-				e = Stack[bp];
+				e = fl->Stack[bp];
 				assert(isvector(e));
 				assert(i < vector_size(e));
 				v = vector_elt(e, i);
 			}else{
-				v = Stack[bp+i];
+				v = fl->Stack[bp+i];
 			}
 			PUSH(v);
 			NEXT_OP;
@@ -1070,7 +1018,7 @@
 		OP(OP_LOADC)
 			s = *ip++;
 			i = *ip++;
-			v = Stack[bp+nargs];
+			v = fl->Stack[bp+nargs];
 			while(s--)
 				v = vector_elt(v, vector_size(v)-1);
 			assert(isvector(v));
@@ -1080,35 +1028,35 @@
 
 		OP(OP_RET)
 			v = POP();
-			SP = curr_frame;
-			curr_frame = Stack[SP-4];
-			if(curr_frame == top_frame)
+			fl->SP = fl->curr_frame;
+			fl->curr_frame = fl->Stack[fl->SP-4];
+			if(fl->curr_frame == top_frame)
 				return v;
-			SP -= 5+nargs;
-			captured = Stack[curr_frame-1];
-			ip = (uint8_t*)Stack[curr_frame-2];
-			nargs = Stack[curr_frame-3];
-			bp = curr_frame - 5 - nargs;
-			Stack[SP-1] = v;
+			fl->SP -= 5+nargs;
+			captured = fl->Stack[fl->curr_frame-1];
+			ip = (uint8_t*)fl->Stack[fl->curr_frame-2];
+			nargs = fl->Stack[fl->curr_frame-3];
+			bp = fl->curr_frame - 5 - nargs;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_DUP)
-			SP++;
-			Stack[SP-1] = Stack[SP-2];
+			fl->SP++;
+			fl->Stack[fl->SP-1] = fl->Stack[fl->SP-2];
 			NEXT_OP;
 
 		OP(OP_CAR)
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			if(!iscons(v))
 				type_error("cons", v);
-			Stack[SP-1] = car_(v);
+			fl->Stack[fl->SP-1] = car_(v);
 			NEXT_OP;
 
 		OP(OP_CDR)
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			if(!iscons(v))
 				type_error("cons", v);
-			Stack[SP-1] = cdr_(v);
+			fl->Stack[fl->SP-1] = cdr_(v);
 			NEXT_OP;
 
 		OP(OP_CLOSURE)
@@ -1121,41 +1069,41 @@
 				pv[0] = fixnum(n+1);
 				pv++;
 				do{
-					pv[n] = Stack[bp+n];
+					pv[n] = fl->Stack[bp+n];
 				}while(n--);
 				// environment representation changed; install
 				// the new representation so everybody can see it
 				captured = 1;
-				Stack[curr_frame-1] = 1;
-				Stack[bp] = Stack[SP-1];
+				fl->Stack[fl->curr_frame-1] = 1;
+				fl->Stack[bp] = fl->Stack[fl->SP-1];
 			}else{
-				PUSH(Stack[bp]); // env has already been captured; share
+				PUSH(fl->Stack[bp]); // env has already been captured; share
 			}
-			if(curheap > lim-2)
+			if(fl->curheap > fl->lim-2)
 				gc(0);
-			pv = (value_t*)curheap;
-			curheap += (4*sizeof(value_t));
-			e = Stack[SP-2];  // closure to copy
+			pv = (value_t*)fl->curheap;
+			fl->curheap += (4*sizeof(value_t));
+			e = fl->Stack[fl->SP-2];  // closure to copy
 			assert(isfunction(e));
 			pv[0] = ((value_t*)ptr(e))[0];
 			pv[1] = ((value_t*)ptr(e))[1];
-			pv[2] = Stack[SP-1];  // env
+			pv[2] = fl->Stack[fl->SP-1];  // env
 			pv[3] = ((value_t*)ptr(e))[3];
 			POPN(1);
-			Stack[SP-1] = tagptr(pv, TAG_FUNCTION);
+			fl->Stack[fl->SP-1] = tagptr(pv, TAG_FUNCTION);
 			NEXT_OP;
 
 		OP(OP_SETA)
 			assert(nargs > 0);
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			i = *ip++;
 			if(captured){
-				e = Stack[bp];
+				e = fl->Stack[bp];
 				assert(isvector(e));
 				assert(i < vector_size(e));
 				vector_elt(e, i) = v;
 			}else{
-				Stack[bp+i] = v;
+				fl->Stack[bp+i] = v;
 			}
 			NEXT_OP;
 
@@ -1164,20 +1112,20 @@
 			NEXT_OP;
 
 		OP(OP_LOADC00)
-			PUSH(vector_elt(Stack[bp+nargs], 0));
+			PUSH(vector_elt(fl->Stack[bp+nargs], 0));
 			NEXT_OP;
 
 		OP(OP_PAIRP)
-			Stack[SP-1] = iscons(Stack[SP-1]) ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = iscons(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_BRNE)
-			ip += Stack[SP-2] != Stack[SP-1] ? GET_INT16(ip) : 2;
+			ip += fl->Stack[fl->SP-2] != fl->Stack[fl->SP-1] ? GET_INT16(ip) : 2;
 			POPN(2);
 			NEXT_OP;
 
 		OP(OP_LOADT)
-			PUSH(FL_T);
+			PUSH(fl->FL_T);
 			NEXT_OP;
 
 		OP(OP_LOAD0)
@@ -1185,36 +1133,36 @@
 			NEXT_OP;
 
 		OP(OP_LOADC01)
-			PUSH(vector_elt(Stack[bp+nargs], 1));
+			PUSH(vector_elt(fl->Stack[bp+nargs], 1));
 			NEXT_OP;
 
 		OP(OP_AREF)
-			v = Stack[SP-2];
+			v = fl->Stack[fl->SP-2];
 			if(isvector(v)){
-				e = Stack[SP-1];
+				e = fl->Stack[fl->SP-1];
 				i = isfixnum(e) ? numval(e) : (uint32_t)toulong(e);
 				if(i >= vector_size(v))
 					bounds_error(v, e);
 				v = vector_elt(v, i);
 			}else if(isarray(v)){
-				v = cvalue_array_aref(&Stack[SP-2]);
+				v = cvalue_array_aref(&fl->Stack[fl->SP-2]);
 			}else{
 				type_error("sequence", v);
 			}
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_ATOMP)
-			Stack[SP-1] = iscons(Stack[SP-1]) ? FL_F : FL_T;
+			fl->Stack[fl->SP-1] = iscons(fl->Stack[fl->SP-1]) ? fl->FL_F : fl->FL_T;
 			NEXT_OP;
 
 		OP(OP_BRT)
-			ip += POP() != FL_F ? GET_INT16(ip) : 2;
+			ip += POP() != fl->FL_F ? GET_INT16(ip) : 2;
 			NEXT_OP;
 
 		OP(OP_BRNN)
-			ip += POP() != NIL ? GET_INT16(ip) : 2;
+			ip += POP() != fl->NIL ? GET_INT16(ip) : 2;
 			NEXT_OP;
 
 		OP(OP_LOAD1)
@@ -1222,108 +1170,108 @@
 			NEXT_OP;
 
 		OP(OP_LT)
-			x = numeric_compare(Stack[SP-2], Stack[SP-1], 0, 0, 0);
+			x = numeric_compare(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 0, 0, 0);
 			if(x > 1)
-				x = numval(fl_compare(Stack[SP-2], Stack[SP-1]));
+				x = numval(fl_compare(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1]));
 			POPN(1);
-			Stack[SP-1] = x < 0 ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = x < 0 ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_ADD2)
-			if(bothfixnums(Stack[SP-1], Stack[SP-2])){
-				s = numval(Stack[SP-1]) + numval(Stack[SP-2]);
+			if(bothfixnums(fl->Stack[fl->SP-1], fl->Stack[fl->SP-2])){
+				s = numval(fl->Stack[fl->SP-1]) + numval(fl->Stack[fl->SP-2]);
 				v = fits_fixnum(s) ? fixnum(s) : mk_xlong(s);
 			}else{
-				v = fl_add_any(&Stack[SP-2], 2, 0);
+				v = fl_add_any(&fl->Stack[fl->SP-2], 2, 0);
 			}
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_SETCDR)
-			cdr(Stack[SP-2]) = Stack[SP-1];
+			cdr(fl->Stack[fl->SP-2]) = fl->Stack[fl->SP-1];
 			POPN(1);
 			NEXT_OP;
 
 		OP(OP_LOADF)
-			PUSH(FL_F);
+			PUSH(fl->FL_F);
 			NEXT_OP;
 
 		OP(OP_CONS)
-			if(curheap > lim)
+			if(fl->curheap > fl->lim)
 				gc(0);
-			c = (cons_t*)curheap;
-			curheap += sizeof(cons_t);
-			c->car = Stack[SP-2];
-			c->cdr = Stack[SP-1];
-			Stack[SP-2] = tagptr(c, TAG_CONS);
+			c = (cons_t*)fl->curheap;
+			fl->curheap += sizeof(cons_t);
+			c->car = fl->Stack[fl->SP-2];
+			c->cdr = fl->Stack[fl->SP-1];
+			fl->Stack[fl->SP-2] = tagptr(c, TAG_CONS);
 			POPN(1); NEXT_OP;
 
 		OP(OP_EQ)
-			Stack[SP-2] = Stack[SP-2] == Stack[SP-1] ? FL_T : FL_F;
+			fl->Stack[fl->SP-2] = fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1] ? fl->FL_T : fl->FL_F;
 			POPN(1);
 			NEXT_OP;
 
 		OP(OP_SYMBOLP)
-			Stack[SP-1] = issymbol(Stack[SP-1]) ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = issymbol(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_NOT)
-			Stack[SP-1] = Stack[SP-1] == FL_F ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = fl->Stack[fl->SP-1] == fl->FL_F ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_CADR)
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			if(!iscons(v))
 				type_error("cons", v);
 			v = cdr_(v);
 			if(!iscons(v))
 				type_error("cons", v);
-			Stack[SP-1] = car_(v);
+			fl->Stack[fl->SP-1] = car_(v);
 			NEXT_OP;
 
 		OP(OP_NEG)
 		do_neg:
-			Stack[SP-1] = fl_neg(Stack[SP-1]);
+			fl->Stack[fl->SP-1] = fl_neg(fl->Stack[fl->SP-1]);
 			NEXT_OP;
 
 		OP(OP_NULLP)
-			Stack[SP-1] = Stack[SP-1] == NIL ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = fl->Stack[fl->SP-1] == fl->NIL ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_BOOLEANP)
-			v = Stack[SP-1];
-			Stack[SP-1] = (v == FL_T || v == FL_F) ? FL_T : FL_F;
+			v = fl->Stack[fl->SP-1];
+			fl->Stack[fl->SP-1] = (v == fl->FL_T || v == fl->FL_F) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_NUMBERP)
-			v = Stack[SP-1];
-			Stack[SP-1] = fl_isnumber(v) ? FL_T:FL_F;
+			v = fl->Stack[fl->SP-1];
+			fl->Stack[fl->SP-1] = fl_isnumber(v) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_FIXNUMP)
-			Stack[SP-1] = isfixnum(Stack[SP-1]) ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = isfixnum(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_BOUNDP)
-			sym = tosymbol(Stack[SP-1]);
-			Stack[SP-1] = sym->binding == UNBOUND ? FL_F : FL_T;
+			sym = tosymbol(fl->Stack[fl->SP-1]);
+			fl->Stack[fl->SP-1] = sym->binding == UNBOUND ? fl->FL_F : fl->FL_T;
 			NEXT_OP;
 
 		OP(OP_BUILTINP)
-			v = Stack[SP-1];
-			Stack[SP-1] = (isbuiltin(v) || iscbuiltin(v)) ? FL_T : FL_F;
+			v = fl->Stack[fl->SP-1];
+			fl->Stack[fl->SP-1] = (isbuiltin(v) || iscbuiltin(v)) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_FUNCTIONP)
-			v = Stack[SP-1];
-			Stack[SP-1] = ((tag(v) == TAG_FUNCTION &&
+			v = fl->Stack[fl->SP-1];
+			fl->Stack[fl->SP-1] = ((tag(v) == TAG_FUNCTION &&
 							(isbuiltin(v) || v>(N_BUILTINS<<3))) ||
-						   iscbuiltin(v)) ? FL_T : FL_F;
+						   iscbuiltin(v)) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_VECTORP)
-			Stack[SP-1] = isvector(Stack[SP-1]) ? FL_T : FL_F;
+			fl->Stack[fl->SP-1] = isvector(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F;
 			NEXT_OP;
 
 		OP(OP_JMPL)
@@ -1331,52 +1279,52 @@
 			NEXT_OP;
 
 		OP(OP_BRFL)
-			ip += POP() == FL_F ? GET_INT32(ip) : 4;
+			ip += POP() == fl->FL_F ? GET_INT32(ip) : 4;
 			NEXT_OP;
 
 		OP(OP_BRTL)
-			ip += POP() != FL_F ? GET_INT32(ip) : 4;
+			ip += POP() != fl->FL_F ? GET_INT32(ip) : 4;
 			NEXT_OP;
 
 		OP(OP_BRNEL)
-			ip += Stack[SP-2] != Stack[SP-1] ? GET_INT32(ip) : 4;
+			ip += fl->Stack[fl->SP-2] != fl->Stack[fl->SP-1] ? GET_INT32(ip) : 4;
 			POPN(2);
 			NEXT_OP;
 
 		OP(OP_BRNNL)
-			ip += POP() != NIL ? GET_INT32(ip) : 4;
+			ip += POP() != fl->NIL ? GET_INT32(ip) : 4;
 			NEXT_OP;
 
 		OP(OP_BRN)
-			ip += POP() == NIL ? GET_INT16(ip) : 2;
+			ip += POP() == fl->NIL ? GET_INT16(ip) : 2;
 			NEXT_OP;
 
 		OP(OP_BRNL)
-			ip += POP() == NIL ? GET_INT32(ip) : 4;
+			ip += POP() == fl->NIL ? GET_INT32(ip) : 4;
 			NEXT_OP;
 
 		OP(OP_EQV)
-			if(Stack[SP-2] == Stack[SP-1])
-				v = FL_T;
-			else if(!leafp(Stack[SP-2]) || !leafp(Stack[SP-1]))
-				v = FL_F;
+			if(fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1])
+				v = fl->FL_T;
+			else if(!leafp(fl->Stack[fl->SP-2]) || !leafp(fl->Stack[fl->SP-1]))
+				v = fl->FL_F;
 			else
-				v = compare_(Stack[SP-2], Stack[SP-1], 1) == 0 ? FL_T : FL_F;
-			Stack[SP-2] = v;
+				v = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 1) == 0 ? fl->FL_T : fl->FL_F;
+			fl->Stack[fl->SP-2] = v;
 			POPN(1);
 			NEXT_OP;
 
 		OP(OP_EQUAL)
-			if(Stack[SP-2] == Stack[SP-1])
-				v = FL_T;
+			if(fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1])
+				v = fl->FL_T;
 			else
-				v = compare_(Stack[SP-2], Stack[SP-1], 1) == 0 ? FL_T : FL_F;
-			Stack[SP-2] = v;
+				v = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 1) == 0 ? fl->FL_T : fl->FL_F;
+			fl->Stack[fl->SP-2] = v;
 			POPN(1);
 			NEXT_OP;
 
 		OP(OP_SETCAR)
-			car(Stack[SP-2]) = Stack[SP-1];
+			car(fl->Stack[fl->SP-2]) = fl->Stack[fl->SP-1];
 			POPN(1);
 			NEXT_OP;
 
@@ -1384,11 +1332,11 @@
 			n = *ip++;
 		apply_list:
 			if(n > 0){
-				v = list(&Stack[SP-n], n, 0);
+				v = list(&fl->Stack[fl->SP-n], n, 0);
 				POPN(n);
 				PUSH(v);
 			}else{
-				PUSH(NIL);
+				PUSH(fl->NIL);
 			}
 			NEXT_OP;
 
@@ -1401,14 +1349,14 @@
 			n = *ip++;
 		apply_apply:
 			v = POP();	 // arglist
-			n = SP-(n-2);  // n-2 == # leading arguments not in the list
+			n = fl->SP-(n-2);  // n-2 == # leading arguments not in the list
 			while(iscons(v)){
-				if(SP >= N_STACK)
+				if(fl->SP >= fl->N_STACK)
 					grow_stack();
 				PUSH(car_(v));
 				v = cdr_(v);
 			}
-			n = SP-n;
+			n = fl->SP-n;
 			goto do_call;
 
 		OP(OP_ADD)
@@ -1415,10 +1363,10 @@
 			n = *ip++;
 		apply_add:
 			s = 0;
-			i = SP-n;
-			for(; i < SP; i++){
-				if(isfixnum(Stack[i])){
-					s += numval(Stack[i]);
+			i = fl->SP-n;
+			for(; i < fl->SP; i++){
+				if(isfixnum(fl->Stack[i])){
+					s += numval(fl->Stack[i]);
 					if(!fits_fixnum(s)){
 						i++;
 						goto add_ovf;
@@ -1425,11 +1373,11 @@
 					}
 				}else{
 				add_ovf:
-					v = fl_add_any(&Stack[i], SP-i, s);
+					v = fl_add_any(&fl->Stack[i], fl->SP-i, s);
 					break;
 				}
 			}
-			if(i == SP)
+			if(i == fl->SP)
 				v = fixnum(s);
 			POPN(n);
 			PUSH(v);
@@ -1442,14 +1390,14 @@
 				goto do_sub2;
 			if(n == 1)
 				goto do_neg;
-			i = SP-n;
+			i = fl->SP-n;
 			// we need to pass the full arglist on to fl_add_any
 			// so it can handle rest args properly
-			PUSH(Stack[i]);
-			Stack[i] = fixnum(0);
-			Stack[i+1] = fl_neg(fl_add_any(&Stack[i], n, 0));
-			Stack[i] = POP();
-			v = fl_add_any(&Stack[i], 2, 0);
+			PUSH(fl->Stack[i]);
+			fl->Stack[i] = fixnum(0);
+			fl->Stack[i+1] = fl_neg(fl_add_any(&fl->Stack[i], n, 0));
+			fl->Stack[i] = POP();
+			v = fl_add_any(&fl->Stack[i], 2, 0);
 			POPN(n);
 			PUSH(v);
 			NEXT_OP;
@@ -1456,15 +1404,15 @@
 
 		OP(OP_SUB2)
 		do_sub2:
-			if(bothfixnums(Stack[SP-2], Stack[SP-1])){
-				s = numval(Stack[SP-2]) - numval(Stack[SP-1]);
+			if(bothfixnums(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1])){
+				s = numval(fl->Stack[fl->SP-2]) - numval(fl->Stack[fl->SP-1]);
 				v = fits_fixnum(s) ? fixnum(s) : mk_xlong(s);
 			}else{
-				Stack[SP-1] = fl_neg(Stack[SP-1]);
-				v = fl_add_any(&Stack[SP-2], 2, 0);
+				fl->Stack[fl->SP-1] = fl_neg(fl->Stack[fl->SP-1]);
+				v = fl_add_any(&fl->Stack[fl->SP-2], 2, 0);
 			}
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_MUL)
@@ -1471,15 +1419,15 @@
 			n = *ip++;
 		apply_mul:
 			accum = 1;
-			for(i = SP-n; i < SP; i++){
-				if(isfixnum(Stack[i])){
-					accum *= numval(Stack[i]);
+			for(i = fl->SP-n; i < fl->SP; i++){
+				if(isfixnum(fl->Stack[i])){
+					accum *= numval(fl->Stack[i]);
 				}else{
-					v = fl_mul_any(&Stack[i], SP-i, accum);
+					v = fl_mul_any(&fl->Stack[i], fl->SP-i, accum);
 					break;
 				}
 			}
-			if(i == SP)
+			if(i == fl->SP)
 				v = fits_fixnum(accum) ? fixnum(accum) : return_from_int64(accum);
 			POPN(n);
 			PUSH(v);
@@ -1488,17 +1436,17 @@
 		OP(OP_DIV)
 			n = *ip++;
 		apply_div:
-			i = SP-n;
+			i = fl->SP-n;
 			if(n == 1){
-				Stack[SP-1] = fl_div2(fixnum(1), Stack[i]);
+				fl->Stack[fl->SP-1] = fl_div2(fixnum(1), fl->Stack[i]);
 			}else{
 				if(n > 2){
-					PUSH(Stack[i]);
-					Stack[i] = fixnum(1);
-					Stack[i+1] = fl_mul_any(&Stack[i], n, 1);
-					Stack[i] = POP();
+					PUSH(fl->Stack[i]);
+					fl->Stack[i] = fixnum(1);
+					fl->Stack[i+1] = fl_mul_any(&fl->Stack[i], n, 1);
+					fl->Stack[i] = POP();
 				}
-				v = fl_div2(Stack[i], Stack[i+1]);
+				v = fl_div2(fl->Stack[i], fl->Stack[i+1]);
 				POPN(n);
 				PUSH(v);
 			}
@@ -1505,7 +1453,7 @@
 			NEXT_OP;
 
 		OP(OP_IDIV)
-			v = Stack[SP-2]; e = Stack[SP-1];
+			v = fl->Stack[fl->SP-2]; e = fl->Stack[fl->SP-1];
 			if(bothfixnums(v, e)){
 				if(e == 0)
 					DivideByZeroError();
@@ -1513,21 +1461,21 @@
 			}else
 				v = fl_idiv2(v, e);
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_NUMEQ)
-			v = Stack[SP-2]; e = Stack[SP-1];
+			v = fl->Stack[fl->SP-2]; e = fl->Stack[fl->SP-1];
 			if(bothfixnums(v, e))
-				v = v == e ? FL_T : FL_F;
+				v = v == e ? fl->FL_T : fl->FL_F;
 			else
-				v = numeric_compare(v, e, 1, 0, 1) == 0 ? FL_T : FL_F;
+				v = numeric_compare(v, e, 1, 0, 1) == 0 ? fl->FL_T : fl->FL_F;
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_COMPARE)
-			Stack[SP-2] = compare_(Stack[SP-2], Stack[SP-1], 0);
+			fl->Stack[fl->SP-2] = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 0);
 			POPN(1);
 			NEXT_OP;
 
@@ -1546,7 +1494,7 @@
 		apply_vector:
 			v = alloc_vector(n, 0);
 			if(n){
-				memmove(&vector_elt(v, 0), &Stack[SP-n], n*sizeof(value_t));
+				memmove(&vector_elt(v, 0), &fl->Stack[fl->SP-n], n*sizeof(value_t));
 				POPN(n);
 			}
 			PUSH(v);
@@ -1553,40 +1501,40 @@
 			NEXT_OP;
 
 		OP(OP_ASET)
-			e = Stack[SP-3];
+			e = fl->Stack[fl->SP-3];
 			if(isvector(e)){
-				i = tofixnum(Stack[SP-2]);
+				i = tofixnum(fl->Stack[fl->SP-2]);
 				if(i >= vector_size(e))
-					bounds_error(v, Stack[SP-1]);
-				vector_elt(e, i) = (v = Stack[SP-1]);
+					bounds_error(v, fl->Stack[fl->SP-1]);
+				vector_elt(e, i) = (v = fl->Stack[fl->SP-1]);
 			}else if(isarray(e)){
-				v = cvalue_array_aset(&Stack[SP-3]);
+				v = cvalue_array_aset(&fl->Stack[fl->SP-3]);
 			}else{
 				type_error("sequence", e);
 			}
 			POPN(2);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_FOR)
-			s  = tofixnum(Stack[SP-3]);
-			hi = tofixnum(Stack[SP-2]);
-			//f = Stack[SP-1];
-			v = FL_UNSPECIFIED;
-			SP += 2;
-			n = SP;
+			s  = tofixnum(fl->Stack[fl->SP-3]);
+			hi = tofixnum(fl->Stack[fl->SP-2]);
+			//f = fl->Stack[fl->SP-1];
+			v = fl->FL_UNSPECIFIED;
+			fl->SP += 2;
+			n = fl->SP;
 			for(; s <= hi; s++){
-				Stack[SP-2] = Stack[SP-3];
-				Stack[SP-1] = fixnum(s);
+				fl->Stack[fl->SP-2] = fl->Stack[fl->SP-3];
+				fl->Stack[fl->SP-1] = fixnum(s);
 				v = apply_cl(1);
-				SP = n;
+				fl->SP = n;
 			}
 			POPN(4);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_LOADNIL)
-			PUSH(NIL);
+			PUSH(fl->NIL);
 			NEXT_OP;
 
 		OP(OP_LOADI8)
@@ -1595,7 +1543,7 @@
 			NEXT_OP;
 
 		OP(OP_LOADVL)
-			v = fn_vals(Stack[bp-1]);
+			v = fn_vals(fl->Stack[bp-1]);
 			v = vector_elt(v, GET_INT32(ip));
 			ip += 4;
 			PUSH(v);
@@ -1602,12 +1550,12 @@
 			NEXT_OP;
 
 		OP(OP_SETGL)
-			v = fn_vals(Stack[bp-1]);
+			v = fn_vals(fl->Stack[bp-1]);
 			v = vector_elt(v, GET_INT32(ip));
 			ip += 4;
 			if(0){
 		OP(OP_SETG)
-				v = fn_vals(Stack[bp-1]);
+				v = fn_vals(fl->Stack[bp-1]);
 				assert(*ip < vector_size(v));
 				v = vector_elt(v, *ip);
 				ip++;
@@ -1614,7 +1562,7 @@
 			}
 			assert(issymbol(v));
 			sym = (symbol_t*)ptr(v);
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			if(!isconstant(sym))
 				sym->binding = v;
 			NEXT_OP;
@@ -1623,30 +1571,30 @@
 			assert(nargs > 0);
 			i = GET_INT32(ip);
 			ip += 4;
-			v = captured ? vector_elt(Stack[bp], i) : Stack[bp+i];
+			v = captured ? vector_elt(fl->Stack[bp], i) : fl->Stack[bp+i];
 			PUSH(v);
 			NEXT_OP;
 
 		OP(OP_SETAL)
 			assert(nargs > 0);
-			v = Stack[SP-1];
+			v = fl->Stack[fl->SP-1];
 			i = GET_INT32(ip);
 			ip += 4;
 			if(captured)
-				vector_elt(Stack[bp], i) = v;
+				vector_elt(fl->Stack[bp], i) = v;
 			else
-				Stack[bp+i] = v;
+				fl->Stack[bp+i] = v;
 			NEXT_OP;
 
 		OP(OP_SETC)
 			s = *ip++;
 			i = *ip++;
-			v = Stack[bp+nargs];
+			v = fl->Stack[bp+nargs];
 			while(s--)
 				v = vector_elt(v, vector_size(v)-1);
 			assert(isvector(v));
 			assert(i < vector_size(v));
-			vector_elt(v, i) = Stack[SP-1];
+			vector_elt(v, i) = fl->Stack[fl->SP-1];
 			NEXT_OP;
 
 		OP(OP_LOADCL)
@@ -1654,7 +1602,7 @@
 			ip += 4;
 			i = GET_INT32(ip);
 			ip += 4;
-			v = Stack[bp+nargs];
+			v = fl->Stack[bp+nargs];
 			while(s--)
 				v = vector_elt(v, vector_size(v)-1);
 			PUSH(vector_elt(v, i));
@@ -1665,11 +1613,11 @@
 			ip += 4;
 			i = GET_INT32(ip);
 			ip += 4;
-			v = Stack[bp+nargs];
+			v = fl->Stack[bp+nargs];
 			while(s--)
 				v = vector_elt(v, vector_size(v)-1);
 			assert(i < vector_size(v));
-			vector_elt(v, i) = Stack[SP-1];
+			vector_elt(v, i) = fl->Stack[fl->SP-1];
 			NEXT_OP;
 
 		OP(OP_VARGC)
@@ -1681,26 +1629,26 @@
 			}
 			s = (fixnum_t)nargs - (fixnum_t)i;
 			if(s > 0){
-				v = list(&Stack[bp+i], s, 0);
-				Stack[bp+i] = v;
+				v = list(&fl->Stack[bp+i], s, 0);
+				fl->Stack[bp+i] = v;
 				if(s > 1){
-					Stack[bp+i+1] = Stack[bp+nargs+0];
-					Stack[bp+i+2] = Stack[bp+nargs+1];
-					Stack[bp+i+3] = i+1;
-					//Stack[bp+i+4] = 0;
-					Stack[bp+i+5] = 0;
-					SP =  bp+i+6;
-					curr_frame = SP;
+					fl->Stack[bp+i+1] = fl->Stack[bp+nargs+0];
+					fl->Stack[bp+i+2] = fl->Stack[bp+nargs+1];
+					fl->Stack[bp+i+3] = i+1;
+					//fl->Stack[bp+i+4] = 0;
+					fl->Stack[bp+i+5] = 0;
+					fl->SP =  bp+i+6;
+					fl->curr_frame = fl->SP;
 				}
 			}else if(s < 0){
-				lerrorf(ArgError, "too few arguments");
+				lerrorf(fl->ArgError, "too few arguments");
 			}else{
 				PUSH(0);
-				Stack[SP-3] = i+1;
-				Stack[SP-4] = Stack[SP-5];
-				Stack[SP-5] = Stack[SP-6];
-				Stack[SP-6] = NIL;
-				curr_frame = SP;
+				fl->Stack[fl->SP-3] = i+1;
+				fl->Stack[fl->SP-4] = fl->Stack[fl->SP-5];
+				fl->Stack[fl->SP-5] = fl->Stack[fl->SP-6];
+				fl->Stack[fl->SP-6] = fl->NIL;
+				fl->curr_frame = fl->SP;
 			}
 			nargs = i+1;
 			NEXT_OP;
@@ -1708,7 +1656,7 @@
 		OP(OP_TRYCATCH)
 			v = do_trycatch();
 			POPN(1);
-			Stack[SP-1] = v;
+			fl->Stack[fl->SP-1] = v;
 			NEXT_OP;
 
 		OP(OP_OPTARGS)
@@ -1717,23 +1665,23 @@
 			n = GET_INT32(ip);
 			ip += 4;
 			if(nargs < i)
-				lerrorf(ArgError, "too few arguments");
+				lerrorf(fl->ArgError, "too few arguments");
 			if((int32_t)n > 0){
 				if(nargs > n)
-					lerrorf(ArgError, "too many arguments");
+					lerrorf(fl->ArgError, "too many arguments");
 			}else
 				n = -n;
 			if(n > nargs){
 				n -= nargs;
-				SP += n;
-				Stack[SP-1] = Stack[SP-n-1];
-				Stack[SP-2] = Stack[SP-n-2];
-				Stack[SP-3] = nargs+n;
-				Stack[SP-4] = Stack[SP-n-4];
-				Stack[SP-5] = Stack[SP-n-5];
-				curr_frame = SP;
+				fl->SP += n;
+				fl->Stack[fl->SP-1] = fl->Stack[fl->SP-n-1];
+				fl->Stack[fl->SP-2] = fl->Stack[fl->SP-n-2];
+				fl->Stack[fl->SP-3] = nargs+n;
+				fl->Stack[fl->SP-4] = fl->Stack[fl->SP-n-4];
+				fl->Stack[fl->SP-5] = fl->Stack[fl->SP-n-5];
+				fl->curr_frame = fl->SP;
 				for(i = 0; i < n; i++)
-					Stack[bp+nargs+i] = UNBOUND;
+					fl->Stack[bp+nargs+i] = UNBOUND;
 				nargs += n;
 			}
 			NEXT_OP;
@@ -1741,12 +1689,12 @@
 		OP(OP_BRBOUND)
 			i = GET_INT32(ip);
 			ip += 4;
-			v = captured ? vector_elt(Stack[bp], i) : Stack[bp+i];
-			PUSH(v != UNBOUND ? FL_T : FL_F);
+			v = captured ? vector_elt(fl->Stack[bp], i) : fl->Stack[bp+i];
+			PUSH(v != UNBOUND ? fl->FL_T : fl->FL_F);
 			NEXT_OP;
 
 		OP(OP_KEYARGS)
-			v = fn_vals(Stack[bp-1]);
+			v = fn_vals(fl->Stack[bp-1]);
 			v = vector_elt(v, 0);
 			i = GET_INT32(ip);
 			ip += 4;
@@ -1794,28 +1742,28 @@
 static value_t
 _stacktrace(uint32_t top)
 {
-	value_t lst = NIL;
+	value_t lst = fl->NIL;
 
 	fl_gc_handle(&lst);
 	while(top > 0){
-		uint32_t sz = Stack[top-3]+1;
+		uint32_t sz = fl->Stack[top-3]+1;
 		uint32_t bp = top-5-sz;
 		value_t v = alloc_vector(sz, 0);
-		if(Stack[top-1] /*captured*/){
-			vector_elt(v, 0) = Stack[bp];
+		if(fl->Stack[top-1] /*captured*/){
+			vector_elt(v, 0) = fl->Stack[bp];
 			memmove(&vector_elt(v, 1),
-				   &vector_elt(Stack[bp+1], 0), (sz-1)*sizeof(value_t));
+				   &vector_elt(fl->Stack[bp+1], 0), (sz-1)*sizeof(value_t));
 		}else{
 			uint32_t i;
 			for(i = 0; i < sz; i++){
-				value_t si = Stack[bp+i];
+				value_t si = fl->Stack[bp+i];
 				// if there's an error evaluating argument defaults some slots
 				// might be left set to UNBOUND (issue #22)
-				vector_elt(v, i) = si == UNBOUND ? FL_UNSPECIFIED : si;
+				vector_elt(v, i) = si == UNBOUND ? fl->FL_UNSPECIFIED : si;
 			}
 		}
 		lst = fl_cons(v, lst);
-		top = Stack[top-4];
+		top = fl->Stack[top-4];
 	}
 	fl_free_gc_handles(1);
 	return lst;
@@ -1828,7 +1776,7 @@
 	USED(args);
 	argcount(nargs, 0);
 	gc(0);
-	return FL_T;
+	return fl->FL_T;
 }
 
 BUILTIN("function", function)
@@ -1864,8 +1812,8 @@
 	value_t fv = tagptr(fn, TAG_FUNCTION);
 	fn->bcode = args[0];
 	fn->vals = args[1];
-	fn->env = NIL;
-	fn->name = LAMBDA;
+	fn->env = fl->NIL;
+	fn->name = fl->LAMBDA;
 	if(nargs > 2){
 		if(issymbol(args[2])){
 			fn->name = args[2];
@@ -1880,7 +1828,7 @@
 			}
 		}
 		if(isgensym(fn->name))
-			lerrorf(ArgError, "name should not be a gensym");
+			lerrorf(fl->ArgError, "name should not be a gensym");
 	}
 	return fv;
 }
@@ -1929,10 +1877,10 @@
 
 BUILTIN("append", append)
 {
-	value_t first = NIL, lst, lastcons = NIL;
+	value_t first = fl->NIL, lst, lastcons = fl->NIL;
 	int i;
 	if(nargs == 0)
-		return NIL;
+		return fl->NIL;
 	fl_gc_handle(&first);
 	fl_gc_handle(&lastcons);
 	for(i = 0; i < nargs; i++){
@@ -1939,12 +1887,12 @@
 		lst = args[i];
 		if(iscons(lst)){
 			lst = copy_list(lst);
-			if(first == NIL)
+			if(first == fl->NIL)
 				first = lst;
 			else
 				cdr_(lastcons) = lst;
-			lastcons = tagptr((((cons_t*)curheap)-1), TAG_CONS);
-		}else if(lst != NIL){
+			lastcons = tagptr((((cons_t*)fl->curheap)-1), TAG_CONS);
+		}else if(lst != fl->NIL){
 			type_error("cons", lst);
 		}
 	}
@@ -1965,72 +1913,72 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return _stacktrace(fl_throwing_frame ? fl_throwing_frame : curr_frame);
+	return _stacktrace(fl->throwing_frame ? fl->throwing_frame : fl->curr_frame);
 }
 
 BUILTIN("map", map)
 {
 	if(nargs < 2)
-		lerrorf(ArgError, "too few arguments");
+		lerrorf(fl->ArgError, "too few arguments");
 	if(!iscons(args[1]))
-		return NIL;
+		return fl->NIL;
 	value_t first, last, v;
-	int64_t argSP = args-Stack;
-	assert(argSP >= 0 && argSP < N_STACK);
+	int64_t argSP = args-fl->Stack;
+	assert(argSP >= 0 && argSP < fl->N_STACK);
 	if(nargs == 2){
-		if(SP+3 > N_STACK)
+		if(fl->SP+3 > fl->N_STACK)
 			grow_stack();
-		PUSH(Stack[argSP]);
-		PUSH(car_(Stack[argSP+1]));
+		PUSH(fl->Stack[argSP]);
+		PUSH(car_(fl->Stack[argSP+1]));
 		v = _applyn(1);
 		PUSH(v);
 		v = mk_cons();
-		car_(v) = POP(); cdr_(v) = NIL;
+		car_(v) = POP(); cdr_(v) = fl->NIL;
 		last = first = v;
-		Stack[argSP+1] = cdr_(Stack[argSP+1]);
+		fl->Stack[argSP+1] = cdr_(fl->Stack[argSP+1]);
 		fl_gc_handle(&first);
 		fl_gc_handle(&last);
-		while(iscons(Stack[argSP+1])){
-			Stack[SP-2] = Stack[argSP];
-			Stack[SP-1] = car_(Stack[argSP+1]);
+		while(iscons(fl->Stack[argSP+1])){
+			fl->Stack[fl->SP-2] = fl->Stack[argSP];
+			fl->Stack[fl->SP-1] = car_(fl->Stack[argSP+1]);
 			v = _applyn(1);
 			PUSH(v);
 			v = mk_cons();
-			car_(v) = POP(); cdr_(v) = NIL;
+			car_(v) = POP(); cdr_(v) = fl->NIL;
 			cdr_(last) = v;
 			last = v;
-			Stack[argSP+1] = cdr_(Stack[argSP+1]);
+			fl->Stack[argSP+1] = cdr_(fl->Stack[argSP+1]);
 		}
 		POPN(2);
 		fl_free_gc_handles(2);
 	}else{
 		int i;
-		while(SP+nargs+1 > N_STACK)
+		while(fl->SP+nargs+1 > fl->N_STACK)
 			grow_stack();
-		PUSH(Stack[argSP]);
+		PUSH(fl->Stack[argSP]);
 		for(i = 1; i < nargs; i++){
-			PUSH(car(Stack[argSP+i]));
-			Stack[argSP+i] = cdr_(Stack[argSP+i]);
+			PUSH(car(fl->Stack[argSP+i]));
+			fl->Stack[argSP+i] = cdr_(fl->Stack[argSP+i]);
 		}
 		v = _applyn(nargs-1);
 		POPN(nargs);
 		PUSH(v);
 		v = mk_cons();
-		car_(v) = POP(); cdr_(v) = NIL;
+		car_(v) = POP(); cdr_(v) = fl->NIL;
 		last = first = v;
 		fl_gc_handle(&first);
 		fl_gc_handle(&last);
-		while(iscons(Stack[argSP+1])){
-			PUSH(Stack[argSP]);
+		while(iscons(fl->Stack[argSP+1])){
+			PUSH(fl->Stack[argSP]);
 			for(i = 1; i < nargs; i++){
-				PUSH(car(Stack[argSP+i]));
-				Stack[argSP+i] = cdr_(Stack[argSP+i]);
+				PUSH(car(fl->Stack[argSP+i]));
+				fl->Stack[argSP+i] = cdr_(fl->Stack[argSP+i]);
 			}
 			v = _applyn(nargs-1);
 			POPN(nargs);
 			PUSH(v);
 			v = mk_cons();
-			car_(v) = POP(); cdr_(v) = NIL;
+			car_(v) = POP(); cdr_(v) = fl->NIL;
 			cdr_(last) = v;
 			last = v;
 		}
@@ -2045,7 +1993,7 @@
 		argcount(nargs, 1);
 	double s = nargs > 0 ? todouble(args[0]) : 0;
 	sleep_ms(s * 1000.0);
-	return FL_T;
+	return fl->FL_T;
 }
 
 static const builtinspec_t builtin_fns[] = {
@@ -2061,78 +2009,82 @@
 {
 	int i;
 
-	heapsize = initial_heapsize;
+	fl = LLT_ALLOC(sizeof(*fl));
+	memset(fl, 0, sizeof(*fl));
+	fl->SCR_WIDTH = 80;
 
-	fromspace = LLT_ALLOC(heapsize);
-	tospace   = LLT_ALLOC(heapsize);
-	curheap = fromspace;
-	lim = curheap+heapsize-sizeof(cons_t);
-	consflags = bitvector_new(heapsize/sizeof(cons_t), 1);
-	htable_new(&printconses, 32);
+	fl->heapsize = initial_heapsize;
+
+	fl->fromspace = LLT_ALLOC(fl->heapsize);
+	fl->tospace   = LLT_ALLOC(fl->heapsize);
+	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();
-	N_STACK = 262144;
-	Stack = LLT_ALLOC(N_STACK*sizeof(value_t));
+	fl->N_STACK = 262144;
+	fl->Stack = LLT_ALLOC(fl->N_STACK*sizeof(value_t));
 
-	FL_NIL = NIL = builtin(OP_THE_EMPTY_LIST);
-	FL_T = builtin(OP_BOOL_CONST_T);
-	FL_F = builtin(OP_BOOL_CONST_F);
-	FL_EOF = builtin(OP_EOF_OBJECT);
-	LAMBDA = symbol("λ");
-	FUNCTION = symbol("function");
-	QUOTE = symbol("quote");
-	TRYCATCH = symbol("trycatch");
-	BACKQUOTE = symbol("quasiquote");
-	COMMA = symbol("unquote");
-	COMMAAT = symbol("unquote-splicing");
-	COMMADOT = symbol("unquote-nsplicing");
-	IOError = symbol("io-error");
-	ParseError = symbol("parse-error");
-	TypeError = symbol("type-error");
-	ArgError = symbol("arg-error");
-	UnboundError = symbol("unbound-error");
-	KeyError = symbol("key-error");
-	MemoryError = symbol("memory-error");
-	BoundsError = symbol("bounds-error");
-	DivideError = symbol("divide-error");
-	EnumerationError = symbol("enumeration-error");
-	Error = symbol("error");
-	pairsym = symbol("pair");
-	symbolsym = symbol("symbol");
-	fixnumsym = symbol("fixnum");
-	vectorsym = symbol("vector");
-	builtinsym = symbol("builtin");
-	booleansym = symbol("boolean");
-	nullsym = symbol("null");
-	definesym = symbol("define");
-	defmacrosym = symbol("define-macro");
-	forsym = symbol("for");
-	setqsym = symbol("set!");
-	evalsym = symbol("eval");
-	vu8sym = symbol("vu8");
-	fnsym = symbol("fn");
-	nulsym = symbol("nul");
-	alarmsym = symbol("alarm");
-	backspacesym = symbol("backspace");
-	tabsym = symbol("tab");
-	linefeedsym = symbol("linefeed");
-	vtabsym = symbol("vtab");
-	pagesym = symbol("page");
-	returnsym = symbol("return");
-	escsym = symbol("esc");
-	spacesym = symbol("space");
-	deletesym = symbol("delete");
-	newlinesym = symbol("newline");
-	tsym = symbol("t");
-	Tsym = symbol("T");
-	fsym = symbol("f");
-	Fsym = symbol("F");
-	builtins_table_sym = symbol("*builtins*");
-	set(printprettysym = symbol("*print-pretty*"), FL_T);
-	set(printreadablysym = symbol("*print-readably*"), FL_T);
-	set(printwidthsym = symbol("*print-width*"), fixnum(SCR_WIDTH));
-	set(printlengthsym = symbol("*print-length*"), FL_F);
-	set(printlevelsym = symbol("*print-level*"), FL_F);
-	fl_lasterror = NIL;
+	fl->FL_NIL = fl->NIL = builtin(OP_THE_EMPTY_LIST);
+	fl->FL_T = builtin(OP_BOOL_CONST_T);
+	fl->FL_F = builtin(OP_BOOL_CONST_F);
+	fl->FL_EOF = builtin(OP_EOF_OBJECT);
+	fl->LAMBDA = symbol("λ");
+	fl->FUNCTION = symbol("function");
+	fl->QUOTE = symbol("quote");
+	fl->TRYCATCH = symbol("trycatch");
+	fl->BACKQUOTE = symbol("quasiquote");
+	fl->COMMA = symbol("unquote");
+	fl->COMMAAT = symbol("unquote-splicing");
+	fl->COMMADOT = symbol("unquote-nsplicing");
+	fl->IOError = symbol("io-error");
+	fl->ParseError = symbol("parse-error");
+	fl->TypeError = symbol("type-error");
+	fl->ArgError = symbol("arg-error");
+	fl->UnboundError = symbol("unbound-error");
+	fl->KeyError = symbol("key-error");
+	fl->MemoryError = symbol("memory-error");
+	fl->BoundsError = symbol("bounds-error");
+	fl->DivideError = symbol("divide-error");
+	fl->EnumerationError = symbol("enumeration-error");
+	fl->Error = symbol("error");
+	fl->pairsym = symbol("pair");
+	fl->symbolsym = symbol("symbol");
+	fl->fixnumsym = symbol("fixnum");
+	fl->vectorsym = symbol("vector");
+	fl->builtinsym = symbol("builtin");
+	fl->booleansym = symbol("boolean");
+	fl->nullsym = symbol("null");
+	fl->definesym = symbol("define");
+	fl->defmacrosym = symbol("define-macro");
+	fl->forsym = symbol("for");
+	fl->setqsym = symbol("set!");
+	fl->evalsym = symbol("eval");
+	fl->vu8sym = symbol("vu8");
+	fl->fnsym = symbol("fn");
+	fl->nulsym = symbol("nul");
+	fl->alarmsym = symbol("alarm");
+	fl->backspacesym = symbol("backspace");
+	fl->tabsym = symbol("tab");
+	fl->linefeedsym = symbol("linefeed");
+	fl->vtabsym = symbol("vtab");
+	fl->pagesym = symbol("page");
+	fl->returnsym = symbol("return");
+	fl->escsym = symbol("esc");
+	fl->spacesym = symbol("space");
+	fl->deletesym = symbol("delete");
+	fl->newlinesym = symbol("newline");
+	fl->tsym = symbol("t");
+	fl->Tsym = symbol("T");
+	fl->fsym = symbol("f");
+	fl->Fsym = symbol("F");
+	fl->builtins_table_sym = symbol("*builtins*");
+	set(fl->printprettysym = symbol("*print-pretty*"), fl->FL_T);
+	set(fl->printreadablysym = symbol("*print-readably*"), fl->FL_T);
+	set(fl->printwidthsym = symbol("*print-width*"), fixnum(fl->SCR_WIDTH));
+	set(fl->printlengthsym = symbol("*print-length*"), fl->FL_F);
+	set(fl->printlevelsym = symbol("*print-level*"), fl->FL_F);
+	fl->lasterror = fl->NIL;
 	for(i = 0; i < nelem(builtins); i++){
 		if(builtins[i].name)
 			setc(symbol(builtins[i].name), builtin(i));
@@ -2157,12 +2109,12 @@
 	set(symbol("*os-name*"), symbol("unknown"));
 #endif
 
-	the_empty_vector = tagptr(alloc_words(1), TAG_VECTOR);
-	vector_setsize(the_empty_vector, 0);
+	fl->the_empty_vector = tagptr(alloc_words(1), TAG_VECTOR);
+	vector_setsize(fl->the_empty_vector, 0);
 
 	cvalues_init();
 
-	memory_exception_value = fl_list2(MemoryError, cvalue_static_cstring("out of memory"));
+	fl->memory_exception_value = fl_list2(fl->MemoryError, cvalue_static_cstring("out of memory"));
 	const builtinspec_t *b;
 	for(i = 0, b = builtin_fns; i < nelem(builtin_fns); i++, b++)
 		setc(symbol(b->name), cbuiltin(b->name, b->fptr));
@@ -2177,7 +2129,7 @@
 value_t
 fl_toplevel_eval(value_t expr)
 {
-	return fl_applyn(1, symbol_value(evalsym), expr);
+	return fl_applyn(1, symbol_value(fl->evalsym), expr);
 }
 
 int
@@ -2188,17 +2140,17 @@
 	symbol_t *sym;
 
 	PUSH(sys_image_iostream);
-	saveSP = SP;
+	saveSP = fl->SP;
 	FL_TRY{
 		while(1){
-			e = fl_read_sexpr(Stack[SP-1]);
-			if(ios_eof(value2c(ios_t*, Stack[SP-1])))
+			e = fl_read_sexpr(fl->Stack[fl->SP-1]);
+			if(ios_eof(value2c(ios_t*, fl->Stack[fl->SP-1])))
 				break;
 			if(isfunction(e)){
 				// stage 0 format: series of thunks
 				PUSH(e);
 				(void)_applyn(0);
-				SP = saveSP;
+				fl->SP = saveSP;
 			}else{
 				// stage 1 format: list alternating symbol/value
 				while(iscons(e)){
@@ -2214,11 +2166,11 @@
 	}
 	FL_CATCH_NO_INC{
 		ios_puts("fatal error during bootstrap:\n", ios_stderr);
-		fl_print(ios_stderr, fl_lasterror);
+		fl_print(ios_stderr, fl->lasterror);
 		ios_putc('\n', ios_stderr);
 		return 1;
 	}
-	ios_close(value2c(ios_t*, Stack[SP-1]));
+	ios_close(value2c(ios_t*, fl->Stack[fl->SP-1]));
 	POPN(1);
 	return 0;
 }
--- a/flisp.h
+++ b/flisp.h
@@ -104,10 +104,10 @@
 
 // allocate n consecutive conses
 #define cons_reserve(n) tagptr(alloc_words((n)*2), TAG_CONS)
-#define cons_index(c)  (((cons_t*)ptr(c))-((cons_t*)fromspace))
-#define ismarked(c)     bitvector_get(consflags, cons_index(c))
-#define mark_cons(c)   bitvector_set(consflags, cons_index(c), 1)
-#define unmark_cons(c) bitvector_set(consflags, cons_index(c), 0)
+#define cons_index(c)  (((cons_t*)ptr(c))-((cons_t*)fl->fromspace))
+#define ismarked(c)     bitvector_get(fl->consflags, cons_index(c))
+#define mark_cons(c)   bitvector_set(fl->consflags, cons_index(c), 1)
+#define unmark_cons(c) bitvector_set(fl->consflags, cons_index(c), 0)
 
 #define isforwarded(v) (((value_t*)ptr(v))[0] == TAG_FWD)
 #define forwardloc(v) (((value_t*)ptr(v))[1])
@@ -140,11 +140,11 @@
 #define iskeyword(s) ((s)->flags & FLAG_KEYWORD)
 #define symbol_value(s) (((symbol_t*)ptr(s))->binding)
 #define sym_to_numtype(s) (((symbol_t*)ptr(s))->numtype)
-#define ismanaged(v) ((((uint8_t*)ptr(v)) >= fromspace) && (((uint8_t*)ptr(v)) < fromspace+heapsize))
+#define ismanaged(v) ((((uint8_t*)ptr(v)) >= fl->fromspace) && (((uint8_t*)ptr(v)) < fl->fromspace+fl->heapsize))
 #define isgensym(x)  (issymbol(x) && ismanaged(x))
 #define isfunction(x) (tag(x) == TAG_FUNCTION && (x) > (N_BUILTINS<<3))
 #define isclosure(x) isfunction(x)
-#define iscbuiltin(x) (iscvalue(x) && cv_class(ptr(x)) == builtintype)
+#define iscbuiltin(x) (iscvalue(x) && cv_class(ptr(x)) == fl->builtintype)
 // utility for iterating over all arguments in a builtin
 // i=index, i0=start index, arg = var for each arg, args = arg array
 // assumes "nargs" is the argument count
@@ -154,45 +154,18 @@
 
 #define PUSH(v) \
         do{ \
-                Stack[SP++] = (v); \
+                fl->Stack[fl->SP++] = (v); \
         }while(0)
-#define POP()   (Stack[--SP])
+#define POP()   (fl->Stack[--fl->SP])
 #define POPN(n) \
         do{ \
-                SP -= (n); \
+                fl->SP -= (n); \
         }while(0)
 
-extern value_t *Stack;
-extern uint32_t SP;
-
-extern value_t FL_NIL, FL_T, FL_F, FL_EOF, QUOTE;
-extern value_t NIL, LAMBDA, IF, TRYCATCH;
-extern value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT, FUNCTION;
-
-extern value_t printprettysym, printreadablysym, printwidthsym, printlengthsym;
-extern value_t printlevelsym, builtins_table_sym;
-extern value_t pairsym, symbolsym, fixnumsym, vectorsym, builtinsym, vu8sym;
-extern value_t definesym, defmacrosym, forsym, setqsym;
-extern value_t tsym, Tsym, fsym, Fsym, booleansym, nullsym, evalsym, fnsym;
-extern value_t nulsym, alarmsym, backspacesym, tabsym, linefeedsym, newlinesym;
-extern value_t vtabsym, pagesym, returnsym, escsym, spacesym, deletesym;
-
-extern value_t IOError, ParseError, TypeError, ArgError, MemoryError;
-extern value_t DivideError, BoundsError, Error, KeyError, EnumerationError;
-extern value_t ArgError, IOError, KeyError, MemoryError, EnumerationError;
-
-extern uint8_t *fromspace;
-extern uint32_t heapsize;//bytes
-extern uint8_t *tospace;
-extern uint8_t *curheap;
-extern uint8_t *lim;
-extern uint32_t *consflags;
-
 int isbuiltin(value_t x);
 void fl_init(size_t initial_heapsize);
 int fl_load_system_image(value_t ios);
 
-extern bool fl_exiting;
 _Noreturn void fl_exit(int status);
 
 /* collector */
@@ -227,7 +200,6 @@
 double todouble(value_t a);
 
 /* conses */
-extern value_t the_empty_vector;
 value_t mk_cons(void);
 void *alloc_words(int n);
 
@@ -250,18 +222,13 @@
 	struct _ectx_t *prev;
 }fl_exception_context_t;
 
-extern fl_readstate_t *readstate;
-extern fl_exception_context_t *fl_ctx;
-extern uint32_t fl_throwing_frame;
-extern value_t fl_lasterror;
-
 void free_readstate(fl_readstate_t *rs);
 
 #define FL_TRY_EXTERN \
 	fl_exception_context_t _ctx; int l__tr, l__ca; \
-	fl_savestate(&_ctx); fl_ctx = &_ctx; \
+	fl_savestate(&_ctx); fl->exctx = &_ctx; \
 	if(!setjmp(_ctx.buf)) \
-		for(l__tr=1; l__tr; l__tr=0, (void)(fl_ctx = fl_ctx->prev))
+		for(l__tr=1; l__tr; l__tr=0, (void)(fl->exctx = fl->exctx->prev))
 
 #define FL_CATCH_EXTERN_NO_RESTORE \
 	else \
@@ -282,7 +249,7 @@
 #define argcount(nargs, c) \
 	do{ \
 		if(__unlikely(nargs != c)) \
-			lerrorf(ArgError, "arity mismatch: wanted %d, got %d", c, nargs); \
+			lerrorf(fl->ArgError, "arity mismatch: wanted %d, got %d", c, nargs); \
 	}while(0)
 
 typedef struct {
@@ -335,7 +302,7 @@
 #define cv_len(cv) (((cvalue_t*)(cv))->len)
 #define cv_type(cv) (cv_class(cv)->type)
 #define cv_data(cv) (((cvalue_t*)(cv))->data)
-#define cv_isstr(cv) (cv_class(cv)->eltype == bytetype)
+#define cv_isstr(cv) (cv_class(cv)->eltype == fl->bytetype)
 #define cv_isPOD(cv) (cv_class(cv)->init != nil)
 #define cvalue_data(v) cv_data((cvalue_t*)ptr(v))
 #define cvalue_len(v) cv_len((cvalue_t*)ptr(v))
@@ -353,3 +320,108 @@
 #define BUILTIN_FN(l, c) extern BUILTIN(l, c);
 #include "builtin_fns.h"
 #undef BUILTIN_FN
+
+#define N_GC_HANDLES 1024
+
+typedef struct Fl Fl;
+
+struct Fl {
+	const uint8_t *ip;
+	value_t *Stack;
+	uint32_t SP;
+	uint32_t N_STACK;
+	uint32_t curr_frame;
+	value_t *GCHandleStack[N_GC_HANDLES];
+	uint32_t N_GCHND;
+	symbol_t *symtab;
+
+	uint8_t *fromspace;
+	uint8_t *tospace;
+	uint8_t *curheap;
+	uint8_t *lim;
+	uint32_t heapsize;//bytes
+	uint32_t *consflags;
+
+	fl_readstate_t *readstate;
+
+	// saved execution state for an unwind target
+	fl_exception_context_t *exctx;
+	uint32_t throwing_frame;  // active frame when exception was thrown
+	value_t lasterror;
+
+	value_t FL_NIL, FL_T, FL_F, FL_EOF, QUOTE;
+	value_t NIL, LAMBDA, IF, TRYCATCH;
+	value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT, FUNCTION;
+
+	value_t printwidthsym, printreadablysym, printprettysym, printlengthsym;
+	value_t printlevelsym, builtins_table_sym;
+	value_t pairsym, symbolsym, fixnumsym, vectorsym, builtinsym, vu8sym;
+	value_t definesym, defmacrosym, forsym, setqsym;
+	value_t tsym, Tsym, fsym, Fsym, booleansym, nullsym, evalsym, fnsym;
+	// for reading characters
+	value_t nulsym, alarmsym, backspacesym, tabsym, linefeedsym, newlinesym;
+	value_t vtabsym, pagesym, returnsym, escsym, spacesym, deletesym;
+
+	value_t IOError, ParseError, TypeError, ArgError, MemoryError;
+	value_t DivideError, BoundsError, Error, KeyError, EnumerationError;
+	value_t UnboundError;
+
+	value_t the_empty_vector;
+	value_t memory_exception_value;
+
+	value_t tablesym;
+	fltype_t *tabletype;
+
+	value_t iostreamsym, rdsym, wrsym, apsym, crsym, truncsym;
+	value_t instrsym, outstrsym;
+	fltype_t *iostreamtype;
+
+	value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
+	value_t int64sym, uint64sym, bignumsym;
+	value_t longsym, ulongsym, bytesym, runesym;
+	value_t structsym, arraysym, enumsym, cfunctionsym, voidsym, pointersym;
+	value_t stringtypesym, runestringtypesym, emptystringsym;
+	value_t unionsym, floatsym, doublesym;
+
+	htable_t TypeTable;
+	htable_t reverse_dlsym_lookup_table;
+	fltype_t *mpinttype;
+	fltype_t *int8type, *uint8type;
+	fltype_t *int16type, *uint16type;
+	fltype_t *int32type, *uint32type;
+	fltype_t *int64type, *uint64type;
+	fltype_t *longtype, *ulongtype;
+	fltype_t *floattype, *doubletype;
+	fltype_t *bytetype, *runetype;
+	fltype_t *stringtype, *runestringtype;
+	fltype_t *builtintype;
+
+	size_t malloc_pressure;
+	cvalue_t **Finalizers;
+	size_t nfinalizers;
+	size_t maxfinalizers;
+
+	uint32_t _gensym_ctr;
+	// two static buffers for gensym printing so there can be two
+	// gensym names available at a time, mostly for compare()
+	char gsname[2][16];
+	int gsnameno;
+
+	bool exiting;
+	bool grew;
+
+	value_t fsosym;
+	fltype_t *fsotype;
+
+	htable_t printconses;
+	uint32_t printlabel;
+	int print_pretty;
+	int print_princ;
+	fixnum_t print_length;
+	fixnum_t print_level;
+	fixnum_t P_LEVEL;
+	int SCR_WIDTH;
+	int HPOS, VPOS;
+};
+
+extern __thread Fl *fl;
--- a/flmain.c
+++ b/flmain.c
@@ -13,7 +13,7 @@
 argv_list(int argc, char *argv[])
 {
 	int i;
-	value_t lst = FL_NIL, temp;
+	value_t lst = fl->FL_NIL, temp;
 	fl_gc_handle(&lst);
 	fl_gc_handle(&temp);
 	for(i = argc-1; i >= 0; i--){
@@ -41,7 +41,7 @@
 
 	fl_init(512*1024);
 
-	value_t f = cvalue(iostreamtype, sizeof(ios_t));
+	value_t f = cvalue(fl->iostreamtype, sizeof(ios_t));
 	ios_t *s = value2c(ios_t*, f);
 	ios_static_buffer(s, boot, bootsz);
 
@@ -54,7 +54,7 @@
 	}
 	FL_CATCH_EXTERN_NO_RESTORE{
 		ios_puts("fatal error:\n", ios_stderr);
-		fl_print(ios_stderr, fl_lasterror);
+		fl_print(ios_stderr, fl->lasterror);
 		ios_putc('\n', ios_stderr);
 		break;
 	}
--- a/iostream.c
+++ b/iostream.c
@@ -6,10 +6,6 @@
 #include "read.h"
 #include "iostream.h"
 
-static value_t iostreamsym, rdsym, wrsym, apsym, crsym, truncsym;
-static value_t instrsym, outstrsym;
-fltype_t *iostreamtype;
-
 static void
 print_iostream(value_t v, ios_t *f)
 {
@@ -43,13 +39,13 @@
 static int
 isiostream(value_t v)
 {
-	return iscvalue(v) && cv_class(ptr(v)) == iostreamtype;
+	return iscvalue(v) && cv_class(ptr(v)) == fl->iostreamtype;
 }
 
 BUILTIN("iostream?", iostreamp)
 {
 	argcount(nargs, 1);
-	return isiostream(args[0]) ? FL_T : FL_F;
+	return isiostream(args[0]) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("eof-object", eof_object)
@@ -56,13 +52,13 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return FL_EOF;
+	return fl->FL_EOF;
 }
 
 BUILTIN("eof-object?", eof_objectp)
 {
 	argcount(nargs, 1);
-	return args[0] == FL_EOF ? FL_T : FL_F;
+	return args[0] == fl->FL_EOF ? fl->FL_T : fl->FL_F;
 }
 
 ios_t *
@@ -79,24 +75,24 @@
 		argcount(nargs, 1);
 	int i, r = 0, w = 0, c = 0, t = 0, a = 0;
 	for(i = 1; i < nargs; i++){
-		if(args[i] == rdsym)
+		if(args[i] == fl->rdsym)
 			r = 1;
-		else if(args[i] == wrsym)
+		else if(args[i] == fl->wrsym)
 			w = 1;
-		else if(args[i] == apsym)
+		else if(args[i] == fl->apsym)
 			a = w = 1;
-		else if(args[i] == crsym)
+		else if(args[i] == fl->crsym)
 			c = w = 1;
-		else if(args[i] == truncsym)
+		else if(args[i] == fl->truncsym)
 			t = w = 1;
 	}
 	if((r|w|c|t|a) == 0)
 		r = 1;  // default to reading
-	value_t f = cvalue(iostreamtype, sizeof(ios_t));
+	value_t f = cvalue(fl->iostreamtype, sizeof(ios_t));
 	char *fname = tostring(args[0]);
 	ios_t *s = value2c(ios_t*, f);
 	if(ios_file(s, fname, r, w, c, t) == nil)
-		lerrorf(IOError, "could not open \"%s\"", fname);
+		lerrorf(fl->IOError, "could not open \"%s\"", fname);
 	if(a)
 		ios_seek_end(s);
 	return f;
@@ -106,10 +102,10 @@
 {
 	argcount(nargs, 0);
 	USED(args);
-	value_t f = cvalue(iostreamtype, sizeof(ios_t));
+	value_t f = cvalue(fl->iostreamtype, sizeof(ios_t));
 	ios_t *s = value2c(ios_t*, f);
 	if(ios_mem(s, 0) == nil)
-		lerrorf(MemoryError, "could not allocate stream");
+		lerrorf(fl->MemoryError, "could not allocate stream");
 	return f;
 }
 
@@ -119,7 +115,7 @@
 	if(nargs > 1)
 		argcount(nargs, 1);
 	else if(nargs == 0)
-		arg = symbol_value(instrsym);
+		arg = symbol_value(fl->instrsym);
 	else
 		arg = args[0];
 	ios_t *s = toiostream(arg);
@@ -127,7 +123,7 @@
 	value_t v = fl_read_sexpr(arg);
 	fl_free_gc_handles(1);
 	if(ios_eof(s))
-		return FL_EOF;
+		return fl->FL_EOF;
 	return v;
 }
 
@@ -138,10 +134,10 @@
 	Rune r;
 	int res;
 	if((res = ios_getutf8(s, &r)) == IOS_EOF)
-		//lerrorf(IOError, "end of file reached");
-		return FL_EOF;
+		//lerrorf(fl->IOError, "end of file reached");
+		return fl->FL_EOF;
 	if(res == 0)
-		lerrorf(IOError, "invalid UTF-8 sequence");
+		lerrorf(fl->IOError, "invalid UTF-8 sequence");
 	return mk_rune(r);
 }
 
@@ -152,10 +148,10 @@
 	ios_t *s = toiostream(args[0]);
 	int r = ios_wait(s, nargs > 1 ? todouble(args[1]) : -1);
 	if(r >= 0)
-		return r ? FL_T : FL_F;
+		return r ? fl->FL_T : fl->FL_F;
 	if(r == IOS_EOF)
-		return FL_EOF;
-	lerrorf(IOError, "i/o error");
+		return fl->FL_EOF;
+	lerrorf(fl->IOError, "i/o error");
 }
 
 BUILTIN("io-putc", io_putc)
@@ -162,7 +158,7 @@
 {
 	argcount(nargs, 2);
 	ios_t *s = toiostream(args[0]);
-	if(!iscprim(args[1]) || ((cprim_t*)ptr(args[1]))->type != runetype)
+	if(!iscprim(args[1]) || ((cprim_t*)ptr(args[1]))->type != fl->runetype)
 		type_error("rune", args[1]);
 	Rune r = *(Rune*)cp_data((cprim_t*)ptr(args[1]));
 	return fixnum(ios_pututf8(s, r));
@@ -175,7 +171,7 @@
 	off_t off = tooffset(args[1]);
 	off_t res = ios_skip(s, off);
 	if(res < 0)
-		return FL_F;
+		return fl->FL_F;
 	return sizeof(res) == sizeof(int64_t) ? mk_int64(res) : mk_int32(res);
 }
 
@@ -182,7 +178,7 @@
 BUILTIN("io-flush", io_flush)
 {
 	argcount(nargs, 1);
-	return ios_flush(toiostream(args[0])) == 0 ? FL_T : FL_F;
+	return ios_flush(toiostream(args[0])) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("io-close", io_close)
@@ -189,7 +185,7 @@
 {
 	argcount(nargs, 1);
 	ios_close(toiostream(args[0]));
-	return FL_T;
+	return fl->FL_T;
 }
 
 BUILTIN("io-discardbuffer", io_discardbuffer)
@@ -196,13 +192,13 @@
 {
 	argcount(nargs, 1);
 	ios_purge(toiostream(args[0]));
-	return FL_T;
+	return fl->FL_T;
 }
 
 BUILTIN("io-eof?", io_eofp)
 {
 	argcount(nargs, 1);
-	return ios_eof(toiostream(args[0])) ? FL_T : FL_F;
+	return ios_eof(toiostream(args[0])) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("io-seek", io_seek)
@@ -212,8 +208,8 @@
 	size_t pos = toulong(args[1]);
 	off_t res = ios_seek(s, (off_t)pos);
 	if(res < 0)
-		return FL_F;
-	return FL_T;
+		return fl->FL_F;
+	return fl->FL_T;
 }
 
 BUILTIN("io-pos", io_pos)
@@ -222,7 +218,7 @@
 	ios_t *s = toiostream(args[0]);
 	off_t res = ios_pos(s);
 	if(res < 0)
-		return FL_F;
+		return fl->FL_F;
 	return size_wrap((size_t)res);
 }
 
@@ -231,7 +227,7 @@
 	if(nargs < 1 || nargs > 2)
 		argcount(nargs, 1);
 	ios_t *s;
-	s = nargs == 2 ? toiostream(args[1]) : toiostream(symbol_value(outstrsym));
+	s = nargs == 2 ? toiostream(args[1]) : toiostream(symbol_value(fl->outstrsym));
 	fl_print(s, args[0]);
 	return args[0];
 }
@@ -250,7 +246,7 @@
 	}else{
 		ft = get_type(args[1]);
 		if(ft->eltype != nil && !iscons(cdr_(cdr_(args[1]))))
-			lerrorf(ArgError, "incomplete type");
+			lerrorf(fl->ArgError, "incomplete type");
 		n = ft->size;
 	}
 	value_t cv = cvalue(ft, n);
@@ -260,8 +256,8 @@
 	else data = cp_data(ptr(cv));
 	size_t got = ios_read(s, data, n);
 	if(got < n)
-		//lerrorf(IOError, "end of input reached");
-		return FL_EOF;
+		//lerrorf(fl->IOError, "end of input reached");
+		return fl->FL_EOF;
 	return cv;
 }
 
@@ -282,9 +278,9 @@
 	if(nargs < 2 || nargs > 4)
 		argcount(nargs, 2);
 	ios_t *s = toiostream(args[0]);
-	if(iscprim(args[1]) && ((cprim_t*)ptr(args[1]))->type == runetype){
+	if(iscprim(args[1]) && ((cprim_t*)ptr(args[1]))->type == fl->runetype){
 		if(nargs > 2)
-			lerrorf(ArgError, "offset argument not supported for characters");
+			lerrorf(fl->ArgError, "offset argument not supported for characters");
 		Rune r = *(Rune*)cp_data(ptr(args[1]));
 		return fixnum(ios_pututf8(s, r));
 	}
@@ -303,7 +299,7 @@
 {
 	if(nargs < 1 || nargs > 3)
 		argcount(nargs, 1);
-	ios_t *s = toiostream(symbol_value(outstrsym));
+	ios_t *s = toiostream(symbol_value(fl->outstrsym));
 	uint8_t *data;
 	size_t sz, offs = 0;
 	to_sized_ptr(args[0], &data, &sz);
@@ -313,7 +309,7 @@
 		data += offs;
 	}
 	hexdump(s, data, nb, offs);
-	return FL_T;
+	return fl->FL_T;
 }
 
 static char
@@ -322,8 +318,8 @@
 	size_t uldelim = toulong(arg);
 	if(uldelim > 0x7f){
 		// runes > 0x7f, or anything else > 0xff, are out of range
-		if((iscprim(arg) && cp_class(ptr(arg)) == runetype) || uldelim > 0xff)
-			lerrorf(ArgError, "delimiter out of range");
+		if((iscprim(arg) && cp_class(ptr(arg)) == fl->runetype) || uldelim > 0xff)
+			lerrorf(fl->ArgError, "delimiter out of range");
 	}
 	return (char)uldelim;
 }
@@ -350,7 +346,7 @@
 		((uint8_t*)cv->data)[n] = 0;
 	}
 	if(n == 0 && ios_eof(src))
-		return FL_EOF;
+		return fl->FL_EOF;
 	return str;
 }
 
@@ -388,7 +384,7 @@
 	}else{
 		uint8_t *b = ios_takebuf(st, &n); n--;
 		b[n] = '\0';
-		str = cvalue_from_ref(stringtype, b, n, FL_NIL);
+		str = cvalue_from_ref(fl->stringtype, b, n, fl->FL_NIL);
 		cv_autorelease(ptr(str));
 	}
 	return str;
@@ -399,7 +395,7 @@
 	argcount(nargs, 1);
 	ios_t *src = toiostream(args[0]);
 	if(src->bm != bm_mem)
-		lerrorf(ArgError, "requires memory stream");
+		lerrorf(fl->ArgError, "requires memory stream");
 	return stream_to_string(&args[0]);
 }
 
@@ -406,16 +402,16 @@
 void
 iostream_init(void)
 {
-	iostreamsym = symbol("iostream");
-	rdsym = symbol(":read");
-	wrsym = symbol(":write");
-	apsym = symbol(":append");
-	crsym = symbol(":create");
-	truncsym = symbol(":truncate");
-	instrsym = symbol("*input-stream*");
-	outstrsym = symbol("*output-stream*");
-	iostreamtype = define_opaque_type(iostreamsym, sizeof(ios_t), &iostream_vtable, nil);
-	setc(symbol("*stdout*"), cvalue_from_ref(iostreamtype, ios_stdout, sizeof(ios_t), FL_NIL));
-	setc(symbol("*stderr*"), cvalue_from_ref(iostreamtype, ios_stderr, sizeof(ios_t), FL_NIL));
-	setc(symbol("*stdin*" ), cvalue_from_ref(iostreamtype, ios_stdin, sizeof(ios_t), FL_NIL));
+	fl->iostreamsym = symbol("iostream");
+	fl->rdsym = symbol(":read");
+	fl->wrsym = symbol(":write");
+	fl->apsym = symbol(":append");
+	fl->crsym = symbol(":create");
+	fl->truncsym = symbol(":truncate");
+	fl->instrsym = symbol("*input-stream*");
+	fl->outstrsym = symbol("*output-stream*");
+	fl->iostreamtype = define_opaque_type(fl->iostreamsym, sizeof(ios_t), &iostream_vtable, nil);
+	setc(symbol("*stdout*"), cvalue_from_ref(fl->iostreamtype, ios_stdout, sizeof(ios_t), fl->FL_NIL));
+	setc(symbol("*stderr*"), cvalue_from_ref(fl->iostreamtype, ios_stderr, sizeof(ios_t), fl->FL_NIL));
+	setc(symbol("*stdin*" ), cvalue_from_ref(fl->iostreamtype, ios_stdin, sizeof(ios_t), fl->FL_NIL));
 }
--- a/iostream.h
+++ b/iostream.h
@@ -1,5 +1,3 @@
-extern fltype_t *iostreamtype;
-
 ios_t *toiostream(value_t v);
 value_t stream_to_string(value_t *ps);
 void iostream_init(void);
--- a/plan9/platform.h
+++ b/plan9/platform.h
@@ -3,6 +3,8 @@
 #include <ctype.h>
 #include <mp.h>
 
+#define __thread
+
 #define LLT_ALLOC(n) malloc(n)
 #define LLT_REALLOC(p, n) realloc((p), (n))
 #define LLT_FREE(x) free(x)
--- a/print.c
+++ b/print.c
@@ -7,24 +7,14 @@
 #include "print.h"
 #include "read.h"
 
-htable_t printconses;
-static uint32_t printlabel;
-static int print_pretty;
-static int print_princ;
-static fixnum_t print_length;
-static fixnum_t print_level;
-static fixnum_t P_LEVEL;
-int SCR_WIDTH = 80;
-static int HPOS = 0, VPOS;
-
 static void
 outc(char c, ios_t *f)
 {
 	ios_putc(c, f);
 	if(c == '\n')
-		HPOS = 0;
+		fl->HPOS = 0;
 	else
-		HPOS++;
+		fl->HPOS++;
 }
 
 static void
@@ -31,7 +21,7 @@
 outs(char *s, ios_t *f)
 {
 	ios_puts(s, f);
-	HPOS += u8_strwidth(s);
+	fl->HPOS += u8_strwidth(s);
 }
 
 static void
@@ -38,7 +28,7 @@
 outsn(char *s, ios_t *f, size_t n)
 {
 	ios_write(f, s, n);
-	HPOS += u8_strwidth(s);
+	fl->HPOS += u8_strwidth(s);
 }
 
 static int
@@ -45,12 +35,12 @@
 outindent(int n, ios_t *f)
 {
 	// move back to left margin if we get too indented
-	if(n > SCR_WIDTH-12)
+	if(n > fl->SCR_WIDTH-12)
 		n = 2;
 	int n0 = n;
 	ios_putc('\n', f);
-	VPOS++;
-	HPOS = n;
+	fl->VPOS++;
+	fl->HPOS = n;
 	while(n >= 8){
 		ios_putc('\t', f);
 		n -= 8;
@@ -80,9 +70,9 @@
 	value_t *bp;
 	while(iscons(v)){
 		if(ismarked(v)){
-			bp = (value_t*)ptrhash_bp(&printconses, (void*)v);
+			bp = (value_t*)ptrhash_bp(&fl->printconses, (void*)v);
 			if(*bp == (value_t)HT_NOTFOUND)
-				*bp = fixnum(printlabel++);
+				*bp = fixnum(fl->printlabel++);
 			return;
 		}
 		mark_cons(v);
@@ -92,9 +82,9 @@
 	if(!ismanaged(v) || issymbol(v))
 		return;
 	if(ismarked(v)){
-		bp = (value_t*)ptrhash_bp(&printconses, (void*)v);
+		bp = (value_t*)ptrhash_bp(&fl->printconses, (void*)v);
 		if(*bp == (value_t)HT_NOTFOUND)
-			*bp = fixnum(printlabel++);
+			*bp = fixnum(fl->printlabel++);
 		return;
 	}
 	if(isvector(v)){
@@ -183,8 +173,8 @@
 		return (cv_len((cvalue_t*)ptr(v)) < SMALL_STR_LEN);
 	return (
 		isfixnum(v) || isbuiltin(v) || iscprim(v) ||
-		v == FL_F || v == FL_T ||
-		v == FL_NIL || v == FL_EOF
+		v == fl->FL_F || v == fl->FL_T ||
+		v == fl->FL_NIL || v == fl->FL_EOF
 	);
 }
 
@@ -197,7 +187,7 @@
 		return 1;
 	if(iscons(v)){
 		if(tinyp(car_(v)) &&
-		   (tinyp(cdr_(v)) || (iscons(cdr_(v)) && tinyp(car_(cdr_(v))) && cdr_(cdr_(v)) == NIL)))
+		   (tinyp(cdr_(v)) || (iscons(cdr_(v)) && tinyp(car_(cdr_(v))) && cdr_(cdr_(v)) == fl->NIL)))
 			return 1;
 		return 0;
 	}
@@ -215,8 +205,8 @@
 specialindent(value_t head)
 {
 	// indent these forms 2 spaces, not lined up with the first argument
-	if(head == LAMBDA || head == TRYCATCH || head == definesym ||
-		head == defmacrosym || head == forsym)
+	if(head == fl->LAMBDA || head == fl->TRYCATCH || head == fl->definesym ||
+		head == fl->defmacrosym || head == fl->forsym)
 		return 2;
 	return -1;
 }
@@ -227,7 +217,7 @@
 	// get the width of an expression if we can do so cheaply
 	if(issymbol(v))
 		return u8_strwidth(symbol_name(v));
-	if(iscprim(v) && ptr(v) != nil && cp_class((cprim_t*)ptr(v)) == runetype)
+	if(iscprim(v) && ptr(v) != nil && cp_class((cprim_t*)ptr(v)) == fl->runetype)
 		return 4;
 	return -1;
 }
@@ -251,7 +241,7 @@
 indentafter3(value_t head, value_t v)
 {
 	// for certain X always indent (X a b c) after b
-	return ((head == forsym) && !allsmallp(cdr_(v)));
+	return ((head == fl->forsym) && !allsmallp(cdr_(v)));
 }
 
 static int
@@ -258,7 +248,7 @@
 indentafter2(value_t head, value_t v)
 {
 	// for certain X always indent (X a b) after a
-	return ((head == definesym || head == defmacrosym) &&
+	return ((head == fl->definesym || head == fl->defmacrosym) &&
 			!allsmallp(cdr_(v)));
 }
 
@@ -268,9 +258,9 @@
 	// indent before every subform of a special form, unless every
 	// subform is "small"
 	value_t c = car_(v);
-	if(c == LAMBDA || c == setqsym)
+	if(c == fl->LAMBDA || c == fl->setqsym)
 		return 0;
-	if(c == IF) // TODO: others
+	if(c == fl->IF) // TODO: others
 		return !allsmallp(cdr_(v));
 	return 0;
 }
@@ -290,13 +280,13 @@
 {
 	value_t cd;
 	char *op;
-	if(iscons(cdr_(v)) && cdr_(cdr_(v)) == NIL &&
-		!ptrhash_has(&printconses, (void*)cdr_(v)) &&
-		(((car_(v) == QUOTE)	 && (op = "'"))  ||
-		 ((car_(v) == BACKQUOTE) && (op = "`"))  ||
-		 ((car_(v) == COMMA)	 && (op = ","))  ||
-		 ((car_(v) == COMMAAT)   && (op = ",@")) ||
-		 ((car_(v) == COMMADOT)  && (op = ",.")))){
+	if(iscons(cdr_(v)) && cdr_(cdr_(v)) == fl->NIL &&
+		!ptrhash_has(&fl->printconses, (void*)cdr_(v)) &&
+		(((car_(v) == fl->QUOTE)	 && (op = "'"))  ||
+		 ((car_(v) == fl->BACKQUOTE) && (op = "`"))  ||
+		 ((car_(v) == fl->COMMA)	 && (op = ","))  ||
+		 ((car_(v) == fl->COMMAAT)   && (op = ",@")) ||
+		 ((car_(v) == fl->COMMADOT)  && (op = ",.")))){
 		// special prefix syntax
 		unmark_cons(v);
 		unmark_cons(cdr_(v));
@@ -304,9 +294,9 @@
 		fl_print_child(f, car_(cdr_(v)));
 		return;
 	}
-	int startpos = HPOS;
+	int startpos = fl->HPOS;
 	outc('(', f);
-	int newindent = HPOS, blk = blockindent(v);
+	int newindent = fl->HPOS, blk = blockindent(v);
 	int lastv, n = 0, si, ind, est, always = 0, nextsmall, thistiny;
 	if(!blk)
 		always = indentevery(v);
@@ -316,15 +306,15 @@
 	int n_unindented = 1;
 	while(1){
 		cd = cdr_(v);
-		if(print_length >= 0 && n >= print_length && cd != NIL){
+		if(fl->print_length >= 0 && n >= fl->print_length && cd != fl->NIL){
 			outsn("...)", f, 4);
 			break;
 		}
-		lastv = VPOS;
+		lastv = fl->VPOS;
 		unmark_cons(v);
 		fl_print_child(f, car_(v));
-		if(!iscons(cd) || ptrhash_has(&printconses, (void*)cd)){
-			if(cd != NIL){
+		if(!iscons(cd) || ptrhash_has(&fl->printconses, (void*)cd)){
+			if(cd != fl->NIL){
 				outsn(" . ", f, 3);
 				fl_print_child(f, cd);
 			}
@@ -332,8 +322,8 @@
 			break;
 		}
 
-		if(!print_pretty ||
-			((head == LAMBDA) && n == 0)){
+		if(!fl->print_pretty ||
+			((head == fl->LAMBDA) && n == 0)){
 			// never break line before lambda-list
 			ind = 0;
 		}else{
@@ -340,14 +330,14 @@
 			est = lengthestimate(car_(cd));
 			nextsmall = smallp(car_(cd));
 			thistiny = tinyp(car_(v));
-			ind = (((VPOS > lastv) ||
-					(HPOS>SCR_WIDTH/2 && !nextsmall && !thistiny && n>0)) ||
+			ind = (((fl->VPOS > lastv) ||
+					(fl->HPOS>fl->SCR_WIDTH/2 && !nextsmall && !thistiny && n>0)) ||
 
-				   (HPOS > SCR_WIDTH-4) ||
+				   (fl->HPOS > fl->SCR_WIDTH-4) ||
 
-				   (est != -1 && (HPOS+est > SCR_WIDTH-2)) ||
+				   (est != -1 && (fl->HPOS+est > fl->SCR_WIDTH-2)) ||
 
-				   ((head == LAMBDA) && !nextsmall) ||
+				   ((head == fl->LAMBDA) && !nextsmall) ||
 
 				   (n > 0 && always) ||
 
@@ -371,7 +361,7 @@
 				if(si != -1)
 					newindent = startpos + si;
 				else if(!blk)
-					newindent = HPOS;
+					newindent = fl->HPOS;
 			}
 		}
 		n++;
@@ -385,12 +375,12 @@
 print_circle_prefix(ios_t *f, value_t v)
 {
 	value_t label;
-	if((label = (value_t)ptrhash_get(&printconses, (void*)v)) != (value_t)HT_NOTFOUND){
+	if((label = (value_t)ptrhash_get(&fl->printconses, (void*)v)) != (value_t)HT_NOTFOUND){
 		if(!ismarked(v)){
-			HPOS += ios_printf(f, "#%"PRIdPTR"#", numval(label));
+			fl->HPOS += ios_printf(f, "#%"PRIdPTR"#", numval(label));
 			return 1;
 		}
-		HPOS += ios_printf(f, "#%"PRIdPTR"=", numval(label));
+		fl->HPOS += ios_printf(f, "#%"PRIdPTR"=", numval(label));
 	}
 	if(ismanaged(v))
 		unmark_cons(v);
@@ -401,19 +391,19 @@
 fl_print_child(ios_t *f, value_t v)
 {
 	char *name;
-	if(print_level >= 0 && P_LEVEL >= print_level && (iscons(v) || isvector(v) || isclosure(v))){
+	if(fl->print_level >= 0 && fl->P_LEVEL >= fl->print_level && (iscons(v) || isvector(v) || isclosure(v))){
 		outc('#', f);
 		return;
 	}
-	P_LEVEL++;
+	fl->P_LEVEL++;
 
 	switch(tag(v)){
 	case TAG_NUM: case TAG_NUM1:
-		HPOS += ios_printf(f, "%"PRId64, (int64_t)numval(v));
+		fl->HPOS += ios_printf(f, "%"PRId64, (int64_t)numval(v));
 		break;
 	case TAG_SYM:
 		name = symbol_name(v);
-		if(print_princ)
+		if(fl->print_princ)
 			outs(name, f);
 		else if(ismanaged(v)){
 			outsn("#:", f, 2);
@@ -422,21 +412,21 @@
 			print_symbol_name(f, name);
 		break;
 	case TAG_FUNCTION:
-		if(v == FL_T)
+		if(v == fl->FL_T)
 			outsn("#t", f, 2);
-		else if(v == FL_F)
+		else if(v == fl->FL_F)
 			outsn("#f", f, 2);
-		else if(v == FL_NIL)
+		else if(v == fl->FL_NIL)
 			outsn("()", f, 2);
-		else if(v == FL_EOF)
+		else if(v == fl->FL_EOF)
 			outsn("#<eof>", f, 6);
 		else if(isbuiltin(v)){
-			if(!print_princ)
+			if(!fl->print_princ)
 				outsn("#.", f, 2);
 			outs(builtins[uintval(v)].name, f);
 		}else{
 			assert(isclosure(v));
-			if(!print_princ){
+			if(!fl->print_princ){
 				if(print_circle_prefix(f, v))
 					break;
 				function_t *fn = ptr(v);
@@ -450,11 +440,11 @@
 					data[i] -= 48;
 				outc(' ', f);
 				fl_print_child(f, fn->vals);
-				if(fn->env != NIL){
+				if(fn->env != fl->NIL){
 					outc(' ', f);
 					fl_print_child(f, fn->env);
 				}
-				if(fn->name != LAMBDA){
+				if(fn->name != fl->LAMBDA){
 					outc(' ', f);
 					fl_print_child(f, fn->name);
 				}
@@ -473,26 +463,26 @@
 	case TAG_CVALUE:
 	case TAG_VECTOR:
 	case TAG_CONS:
-		if(!print_princ && print_circle_prefix(f, v))
+		if(!fl->print_princ && print_circle_prefix(f, v))
 			break;
 		if(isvector(v)){
 			outs("#(", f);
-			int newindent = HPOS, est;
+			int newindent = fl->HPOS, est;
 			int i, sz = vector_size(v);
 			for(i = 0; i < sz; i++){
-				if(print_length >= 0 && i >= print_length && i < sz-1){
+				if(fl->print_length >= 0 && i >= fl->print_length && i < sz-1){
 					outsn("...", f, 3);
 					break;
 				}
 				fl_print_child(f, vector_elt(v, i));
 				if(i < sz-1){
-					if(!print_pretty)
+					if(!fl->print_pretty)
 						outc(' ', f);
 					else{
 						est = lengthestimate(vector_elt(v, i+1));
-						if(HPOS > SCR_WIDTH-4 ||
-						   (est != -1 && (HPOS+est > SCR_WIDTH-2)) ||
-						   (HPOS > SCR_WIDTH/2 && !smallp(vector_elt(v, i+1)) && !tinyp(vector_elt(v, i))))
+						if(fl->HPOS > fl->SCR_WIDTH-4 ||
+						   (est != -1 && (fl->HPOS+est > fl->SCR_WIDTH-2)) ||
+						   (fl->HPOS > fl->SCR_WIDTH/2 && !smallp(vector_elt(v, i+1)) && !tinyp(vector_elt(v, i))))
 							newindent = outindent(newindent, f);
 						else
 							outc(' ', f);
@@ -508,7 +498,7 @@
 			print_pair(f, v);
 		break;
 	}
-	P_LEVEL--;
+	fl->P_LEVEL--;
 }
 
 static void
@@ -632,20 +622,20 @@
 static void
 cvalue_printdata(ios_t *f, void *data, size_t len, value_t type, int weak)
 {
-	if(type == bytesym){
+	if(type == fl->bytesym){
 		uint8_t ch = *(uint8_t*)data;
-		if(print_princ)
+		if(fl->print_princ)
 			outc(ch, f);
 		else if(weak)
-			HPOS += ios_printf(f, "0x%hhx", ch);
+			fl->HPOS += ios_printf(f, "0x%hhx", ch);
 		else
-			HPOS += ios_printf(f, "#byte(0x%hhx)", ch);
-	}else if(type == runesym){
+			fl->HPOS += ios_printf(f, "#byte(0x%hhx)", ch);
+	}else if(type == fl->runesym){
 		Rune r = *(Rune*)data;
 		char seq[UTFmax+1];
 		int nb = runetochar(seq, &r);
 		seq[nb] = '\0';
-		if(print_princ){
+		if(fl->print_princ){
 			outsn(seq, f, nb);
 		}else{
 			outsn("#\\", f, 2);
@@ -665,15 +655,15 @@
 				if(u8_iswprint(r))
 					outs(seq, f);
 				else
-					HPOS += ios_printf(f, "x%04x", r);
+					fl->HPOS += ios_printf(f, "x%04x", r);
 				break;
 			}
 		}
-	}else if(type == floatsym || type == doublesym){
+	}else if(type == fl->floatsym || type == fl->doublesym){
 		char buf[64];
 		double d;
 		int ndec;
-		if(type == floatsym){
+		if(type == fl->floatsym){
 			d = (double)*(float*)data;
 			ndec = 8;
 		}else{
@@ -686,8 +676,8 @@
 				rep = signbit(d) ? "-nan.0" : "+nan.0";
 			else
 				rep = signbit(d) ? "-inf.0" : "+inf.0";
-			if(type == floatsym && !print_princ && !weak)
-				HPOS += ios_printf(f, "#%s(%s)", symbol_name(type), rep);
+			if(type == fl->floatsym && !fl->print_princ && !weak)
+				fl->HPOS += ios_printf(f, "#%s(%s)", symbol_name(type), rep);
 			else
 				outs(rep, f);
 		}else if(d == 0){
@@ -695,7 +685,7 @@
 				outsn("-0.0", f, 4);
 			else
 				outsn("0.0", f, 3);
-			if(type == floatsym && !print_princ && !weak)
+			if(type == fl->floatsym && !fl->print_princ && !weak)
 				outc('f', f);
 		}else{
 			snprint_real(buf, sizeof(buf), d, 0, ndec, 3, 10);
@@ -703,26 +693,26 @@
 			outs(buf, f);
 			if(!hasdec)
 				outsn(".0", f, 2);
-			if(type == floatsym && !print_princ && !weak)
+			if(type == fl->floatsym && !fl->print_princ && !weak)
 				outc('f', f);
 		}
 #if defined(ULONG64)
-	}else if(type == uint64sym || type == ulongsym){
+	}else if(type == fl->uint64sym || type == fl->ulongsym){
 #else
-	}else if(type == uint64sym){
+	}else if(type == fl->uint64sym){
 #endif
 		uint64_t ui64 = *(uint64_t*)data;
-		if(weak || print_princ)
-			HPOS += ios_printf(f, "%"PRIu64, ui64);
+		if(weak || fl->print_princ)
+			fl->HPOS += ios_printf(f, "%"PRIu64, ui64);
 		else
-			HPOS += ios_printf(f, "#%s(%"PRIu64")", symbol_name(type), ui64);
-	}else if(type == bignumsym){
+			fl->HPOS += ios_printf(f, "#%s(%"PRIu64")", symbol_name(type), ui64);
+	}else if(type == fl->bignumsym){
 		mpint *i = *(mpint**)data;
 		char *s = mptoa(i, 10, nil, 0);
-		if(weak || print_princ)
-			HPOS += ios_printf(f, "%s", s);
+		if(weak || fl->print_princ)
+			fl->HPOS += ios_printf(f, "%s", s);
 		else
-			HPOS += ios_printf(f, "#%s(%s)", symbol_name(type), s);
+			fl->HPOS += ios_printf(f, "#%s(%s)", symbol_name(type), s);
 		LLT_FREE(s);
 	}else if(issymbol(type)){
 		// handle other integer prims. we know it's smaller than uint64
@@ -730,15 +720,15 @@
 		numerictype_t nt = sym_to_numtype(type);
 		if(valid_numtype(nt)){
 			int64_t i64 = conv_to_int64(data, nt);
-			if(weak || print_princ)
-				HPOS += ios_printf(f, "%"PRId64, i64);
+			if(weak || fl->print_princ)
+				fl->HPOS += ios_printf(f, "%"PRId64, i64);
 			else
-				HPOS += ios_printf(f, "#%s(%"PRId64")", symbol_name(type), i64);
+				fl->HPOS += ios_printf(f, "#%s(%"PRId64")", symbol_name(type), i64);
 		}else{
-			HPOS += ios_printf(f, "#<%s>", symbol_name(type));
+			fl->HPOS += ios_printf(f, "#<%s>", symbol_name(type));
 		}
 	}else if(iscons(type)){
-		if(car_(type) == arraysym){
+		if(car_(type) == fl->arraysym){
 			size_t i;
 			value_t eltype = car(cdr_(type));
 			size_t cnt, elsize;
@@ -751,15 +741,15 @@
 				elsize = ctype_sizeof(eltype, &junk);
 				cnt = elsize ? len/elsize : 0;
 			}
-			if(eltype == bytesym){
-				if(print_princ){
+			if(eltype == fl->bytesym){
+				if(fl->print_princ){
 					ios_write(f, data, len);
 					/*
 					char *nl = llt_memrchr(data, '\n', len);
 					if(nl)
-						HPOS = u8_strwidth(nl+1);
+						fl->HPOS = u8_strwidth(nl+1);
 					else
-						HPOS += u8_strwidth(data);
+						fl->HPOS += u8_strwidth(data);
 					*/
 				}else{
 					outc('"', f);
@@ -767,23 +757,23 @@
 					outc('"', f);
 				}
 				return;
-			}else if(eltype == runesym){
+			}else if(eltype == fl->runesym){
 				char buf[UTFmax];
-				if(!print_princ)
+				if(!fl->print_princ)
 					outc('"', f);
 				for(i = 0; i < cnt; i++, data = (char*)data + elsize){
 					int n = runetochar(buf, (Rune*)data);
-					if(print_princ)
+					if(fl->print_princ)
 						ios_write(f, buf, n);
 					else
 						print_string(f, buf, n);
 				}
-				if(!print_princ)
+				if(!fl->print_princ)
 					outc('"', f);
 				return;
 			}
 			if(!weak){
-				if(eltype == uint8sym){
+				if(eltype == fl->uint8sym){
 					outsn("#vu8(", f, 5);
 				}else{
 					outsn("#array(", f, 7);
@@ -801,7 +791,7 @@
 				data = (char*)data + elsize;
 			}
 			outc(')', f);
-		}else if(car_(type) == enumsym){
+		}else if(car_(type) == fl->enumsym){
 			int n = *(int*)data;
 			value_t syms = car(cdr_(type));
 			assert(isvector(syms));
@@ -811,7 +801,7 @@
 				outc(' ', f);
 			}
 			if(n >= (int)vector_size(syms)){
-				cvalue_printdata(f, data, len, int32sym, 1);
+				cvalue_printdata(f, data, len, fl->int32sym, 1);
 			}else{
 				fl_print_child(f, vector_elt(syms, n));
 			}
@@ -828,13 +818,13 @@
 	void *data = cptr(v);
 	value_t label;
 
-	if(cv_class(cv) == builtintype){
+	if(cv_class(cv) == fl->builtintype){
 		void *fptr = *(void**)data;
-		label = (value_t)ptrhash_get(&reverse_dlsym_lookup_table, cv);
+		label = (value_t)ptrhash_get(&fl->reverse_dlsym_lookup_table, cv);
 		if(label == (value_t)HT_NOTFOUND){
-			HPOS += ios_printf(f, "#<builtin @%p>", fptr);
+			fl->HPOS += ios_printf(f, "#<builtin @%p>", fptr);
 		}else{
-			if(print_princ){
+			if(fl->print_princ){
 				outs(symbol_name(label), f);
 			}else{
 				outsn("#fn(", f, 4);
@@ -854,36 +844,36 @@
 static void
 set_print_width(void)
 {
-	value_t pw = symbol_value(printwidthsym);
+	value_t pw = symbol_value(fl->printwidthsym);
 	if(!isfixnum(pw))
 		return;
-	SCR_WIDTH = numval(pw);
+	fl->SCR_WIDTH = numval(pw);
 }
 
 void
 fl_print(ios_t *f, value_t v)
 {
-	print_pretty = symbol_value(printprettysym) != FL_F;
-	if(print_pretty)
+	fl->print_pretty = symbol_value(fl->printprettysym) != fl->FL_F;
+	if(fl->print_pretty)
 		set_print_width();
-	print_princ = symbol_value(printreadablysym) == FL_F;
-	value_t pl = symbol_value(printlengthsym);
-	print_length = isfixnum(pl) ? numval(pl) : -1;
-	pl = symbol_value(printlevelsym);
-	print_level = isfixnum(pl) ? numval(pl) : -1;
-	P_LEVEL = 0;
+	fl->print_princ = symbol_value(fl->printreadablysym) == fl->FL_F;
+	value_t pl = symbol_value(fl->printlengthsym);
+	fl->print_length = isfixnum(pl) ? numval(pl) : -1;
+	pl = symbol_value(fl->printlevelsym);
+	fl->print_level = isfixnum(pl) ? numval(pl) : -1;
+	fl->P_LEVEL = 0;
 
-	printlabel = 0;
-	if(!print_princ)
+	fl->printlabel = 0;
+	if(!fl->print_princ)
 		print_traverse(v);
-	HPOS = VPOS = 0;
+	fl->HPOS = fl->VPOS = 0;
 
 	fl_print_child(f, v);
 
-	if(print_level >= 0 || print_length >= 0)
-		memset(consflags, 0, 4*bitvector_nwords(heapsize/sizeof(cons_t)));
+	if(fl->print_level >= 0 || fl->print_length >= 0)
+		memset(fl->consflags, 0, 4*bitvector_nwords(fl->heapsize/sizeof(cons_t)));
 
 	if((iscons(v) || isvector(v) || isfunction(v) || iscvalue(v)) &&
-		!fl_isstring(v) && v != FL_T && v != FL_F && v != FL_NIL)
-		htable_reset(&printconses, 32);
+		!fl_isstring(v) && v != fl->FL_T && v != fl->FL_F && v != fl->FL_NIL)
+		htable_reset(&fl->printconses, 32);
 }
--- a/print.h
+++ b/print.h
@@ -1,8 +1,5 @@
 #pragma once
 
-extern htable_t printconses;
-extern int SCR_WIDTH;
-
 void fl_print(ios_t *f, value_t v);
 void print_traverse(value_t v);
 void fl_print_chr(char c, ios_t *f);
--- a/read.c
+++ b/read.c
@@ -85,7 +85,7 @@
 	return x;
 }
 
-#define F value2c(ios_t*, readstate->source)
+#define F value2c(ios_t*, fl->readstate->source)
 
 int
 isnumtok_base(char *tok, value_t *pval, int base)
@@ -203,7 +203,7 @@
 {
 	buf[(*pi)++] = c;
 	if(*pi >= (int)(sizeof(buf)-1))
-		lerrorf(ParseError, "token too long");
+		lerrorf(fl->ParseError, "token too long");
 }
 
 // return: 1 if escaped (forced to be symbol)
@@ -286,7 +286,7 @@
 	else if(c == '#'){
 		ch = ios_getc(F); c = (char)ch;
 		if(ch == IOS_EOF)
-			lerrorf(ParseError, "invalid read macro");
+			lerrorf(fl->ParseError, "invalid read macro");
 		if(c == '.')
 			toktype = TOK_SHARPDOT;
 		else if(c == '\'')
@@ -294,12 +294,12 @@
 		else if(c == '\\'){
 			Rune cval;
 			if(ios_getutf8(F, &cval) == IOS_EOF)
-				lerrorf(ParseError, "end of input in character constant");
+				lerrorf(fl->ParseError, "end of input in character constant");
 			if(cval == 'u' || cval == 'U' || cval == 'x'){
 				read_token('u', 0);
 				if(buf[1] != '\0'){ // not a solitary 'u','U','x'
 					if(!read_numtok(&buf[1], &tokval, 16))
-						lerrorf(ParseError, "invalid hex character constant");
+						lerrorf(fl->ParseError, "invalid hex character constant");
 					cval = numval(tokval);
 				}
 			}else if(cval >= 'a' && cval <= 'z'){
@@ -306,20 +306,20 @@
 				read_token((char)cval, 0);
 				tokval = symbol(buf);
 				if(buf[1] == '\0') USED(cval); /* one character */
-				else if(tokval == nulsym)       cval = 0x00;
-				else if(tokval == alarmsym)     cval = 0x07;
-				else if(tokval == backspacesym) cval = 0x08;
-				else if(tokval == tabsym)       cval = 0x09;
-				else if(tokval == linefeedsym)  cval = 0x0A;
-				else if(tokval == newlinesym)   cval = 0x0A;
-				else if(tokval == vtabsym)      cval = 0x0B;
-				else if(tokval == pagesym)      cval = 0x0C;
-				else if(tokval == returnsym)    cval = 0x0D;
-				else if(tokval == escsym)       cval = 0x1B;
-				else if(tokval == spacesym)     cval = 0x20;
-				else if(tokval == deletesym)    cval = 0x7F;
+				else if(tokval == fl->nulsym)       cval = 0x00;
+				else if(tokval == fl->alarmsym)     cval = 0x07;
+				else if(tokval == fl->backspacesym) cval = 0x08;
+				else if(tokval == fl->tabsym)       cval = 0x09;
+				else if(tokval == fl->linefeedsym)  cval = 0x0A;
+				else if(tokval == fl->newlinesym)   cval = 0x0A;
+				else if(tokval == fl->vtabsym)      cval = 0x0B;
+				else if(tokval == fl->pagesym)      cval = 0x0C;
+				else if(tokval == fl->returnsym)    cval = 0x0D;
+				else if(tokval == fl->escsym)       cval = 0x1B;
+				else if(tokval == fl->spacesym)     cval = 0x20;
+				else if(tokval == fl->deletesym)    cval = 0x7F;
 				else
-					lerrorf(ParseError, "unknown character #\\%s", buf);
+					lerrorf(fl->ParseError, "unknown character #\\%s", buf);
 			}
 			toktype = TOK_NUM;
 			tokval = mk_rune(cval);
@@ -326,7 +326,7 @@
 		}else if(c == '('){
 			toktype = TOK_SHARPOPEN;
 		}else if(c == '<'){
-			lerrorf(ParseError, "unreadable object");
+			lerrorf(fl->ParseError, "unreadable object");
 		}else if(isdigit(c)){
 			read_token(c, 1);
 			c = (char)ios_getc(F);
@@ -335,10 +335,10 @@
 			else if(c == '=')
 				toktype = TOK_LABEL;
 			else
-				lerrorf(ParseError, "invalid label");
+				lerrorf(fl->ParseError, "invalid label");
 			x = strtoll(buf, &end, 10);
 			if(*end != '\0')
-				lerrorf(ParseError, "invalid label");
+				lerrorf(fl->ParseError, "invalid label");
 			tokval = fixnum(x);
 		}else if(c == '!'){
 			// #! single line comment for shbang script support
@@ -353,7 +353,7 @@
 				ch = ios_getc(F);
 			hashpipe_gotc:
 				if(ch == IOS_EOF)
-					lerrorf(ParseError, "eof within comment");
+					lerrorf(fl->ParseError, "eof within comment");
 				if((char)ch == '|'){
 					ch = ios_getc(F);
 					if((char)ch == '#'){
@@ -386,7 +386,7 @@
 			read_token((char)ch, 0);
 			x = strtol(buf, &end, 10);
 			if(*end != '\0' || buf[0] == '\0')
-				lerrorf(ParseError, "invalid gensym label");
+				lerrorf(fl->ParseError, "invalid gensym label");
 			toktype = TOK_GENSYM;
 			tokval = fixnum(x);
 		}else if(symchar(c)){
@@ -397,7 +397,7 @@
 			    (c == 'd' && (base = 10)) ||
 			    (c == 'x' && (base = 16))) && (isdigit_base(buf[1], base) || buf[1] == '-')){
 				if(!read_numtok(&buf[1], &tokval, base))
-					lerrorf(ParseError, "invalid base %d constant", base);
+					lerrorf(fl->ParseError, "invalid base %d constant", base);
 				return (toktype = TOK_NUM);
 			}
 
@@ -404,7 +404,7 @@
 			toktype = TOK_SHARPSYM;
 			tokval = symbol(buf);
 		}else{
-			lerrorf(ParseError, "unknown read macro");
+			lerrorf(fl->ParseError, "unknown read macro");
 		}
 	}else if(c == ','){
 		toktype = TOK_COMMA;
@@ -441,11 +441,11 @@
 	PUSH(v);
 	assert(s+d > s);
 	value_t newv = alloc_vector(s+d, 1);
-	v = Stack[SP-1];
+	v = fl->Stack[fl->SP-1];
 	for(i = 0; i < s; i++)
 		vector_elt(newv, i) = vector_elt(v, i);
 	// use gc to rewrite references from the old vector to the new
-	Stack[SP-1] = newv;
+	fl->Stack[fl->SP-1] = newv;
 	if(s > 0){
 		((size_t*)ptr(v))[0] |= 0x1;
 		vector_elt(v, 0) = newv;
@@ -457,21 +457,21 @@
 static value_t
 read_vector(value_t label, uint32_t closer)
 {
-	value_t v = the_empty_vector, elt;
+	value_t v = fl->the_empty_vector, elt;
 	uint32_t i = 0;
 	PUSH(v);
 	if(label != UNBOUND)
-		ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
+		ptrhash_put(&fl->readstate->backrefs, (void*)label, (void*)v);
 	while(peek() != closer){
 		if(ios_eof(F))
-			lerrorf(ParseError, "unexpected end of input");
+			lerrorf(fl->ParseError, "unexpected end of input");
 		if(i >= vector_size(v)){
-			v = Stack[SP-1] = vector_grow(v);
+			v = fl->Stack[fl->SP-1] = vector_grow(v);
 			if(label != UNBOUND)
-				ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
+				ptrhash_put(&fl->readstate->backrefs, (void*)label, (void*)v);
 		}
 		elt = do_read_sexpr(UNBOUND);
-		v = Stack[SP-1];
+		v = fl->Stack[fl->SP-1];
 		assert(i < vector_size(v));
 		vector_elt(v, i) = elt;
 		i++;
@@ -499,7 +499,7 @@
 			temp = LLT_REALLOC(buf, sz);
 			if(temp == nil){
 				LLT_FREE(buf);
-				lerrorf(ParseError, "out of memory reading string");
+				lerrorf(fl->ParseError, "out of memory reading string");
 			}
 			buf = temp;
 		}
@@ -506,7 +506,7 @@
 		c = ios_getc(F);
 		if(c == IOS_EOF){
 			LLT_FREE(buf);
-			lerrorf(ParseError, "unexpected end of input in string");
+			lerrorf(fl->ParseError, "unexpected end of input in string");
 		}
 		if(c == '"')
 			break;
@@ -514,7 +514,7 @@
 			c = ios_getc(F);
 			if(c == IOS_EOF){
 				LLT_FREE(buf);
-				lerrorf(ParseError, "end of input in escape sequence");
+				lerrorf(fl->ParseError, "end of input in escape sequence");
 			}
 			j = 0;
 			if(octal_digit(c)){
@@ -542,7 +542,7 @@
 					r = strtol(eseq, nil, 16);
 				if(!j || r > Runemax){
 					LLT_FREE(buf);
-					lerrorf(ParseError, "invalid escape sequence");
+					lerrorf(fl->ParseError, "invalid escape sequence");
 				}
 				if(ndig == 2)
 					buf[i++] = (char)r;
@@ -554,7 +554,7 @@
 				char esc = read_escape_control_char((char)c);
 				if(esc == (char)c && !strchr("\\'\"`", esc)){
 					LLT_FREE(buf);
-					lerrorf(ParseError, "invalid escape sequence: \\%c", (char)c);
+					lerrorf(fl->ParseError, "invalid escape sequence: \\%c", (char)c);
 				}
 				buf[i++] = esc;
 			}
@@ -577,19 +577,19 @@
 	value_t c, *pc;
 	uint32_t t;
 
-	PUSH(NIL);
-	pc = &Stack[SP-1];  // to keep track of current cons cell
+	PUSH(fl->NIL);
+	pc = &fl->Stack[fl->SP-1];  // to keep track of current cons cell
 	t = peek();
 	while(t != closer){
 		if(ios_eof(F))
-			lerrorf(ParseError, "unexpected end of input");
-		c = mk_cons(); car_(c) = cdr_(c) = NIL;
+			lerrorf(fl->ParseError, "unexpected end of input");
+		c = mk_cons(); car_(c) = cdr_(c) = fl->NIL;
 		if(iscons(*pc))
 			cdr_(*pc) = c;
 		else{
 			*pval = c;
 			if(label != UNBOUND)
-				ptrhash_put(&readstate->backrefs, (void*)label, (void*)c);
+				ptrhash_put(&fl->readstate->backrefs, (void*)label, (void*)c);
 		}
 		*pc = c;
 		c = do_read_sexpr(UNBOUND); // must be on separate lines due to
@@ -602,11 +602,11 @@
 			cdr_(*pc) = c;
 			t = peek();
 			if(ios_eof(F))
-				lerrorf(ParseError, "unexpected end of input");
+				lerrorf(fl->ParseError, "unexpected end of input");
 			if(t != closer){
 				take();
 				lerrorf(
-					ParseError,
+					fl->ParseError,
 					"expected '%c'",
 					closer == TOK_CLOSEB ? ']' : (closer == TOK_CLOSEC ? '}' : ')')
 				);
@@ -631,71 +631,71 @@
 	take();
 	switch(t){
 	case TOK_CLOSE:
-		lerrorf(ParseError, "unexpected ')'");
+		lerrorf(fl->ParseError, "unexpected ')'");
 	case TOK_CLOSEB:
-		lerrorf(ParseError, "unexpected ']'");
+		lerrorf(fl->ParseError, "unexpected ']'");
 	case TOK_CLOSEC:
-		lerrorf(ParseError, "unexpected '}'");
+		lerrorf(fl->ParseError, "unexpected '}'");
 	case TOK_DOT:
-		lerrorf(ParseError, "unexpected '.'");
+		lerrorf(fl->ParseError, "unexpected '.'");
 	case TOK_SYM:
 	case TOK_NUM:
 		return tokval;
 	case TOK_COMMA:
-		head = &COMMA; goto listwith;
+		head = &fl->COMMA; goto listwith;
 	case TOK_COMMAAT:
-		head = &COMMAAT; goto listwith;
+		head = &fl->COMMAAT; goto listwith;
 	case TOK_COMMADOT:
-		head = &COMMADOT; goto listwith;
+		head = &fl->COMMADOT; goto listwith;
 	case TOK_BQ:
-		head = &BACKQUOTE; goto listwith;
+		head = &fl->BACKQUOTE; goto listwith;
 	case TOK_QUOTE:
-		head = &QUOTE;
+		head = &fl->QUOTE;
 	listwith:
 		v = cons_reserve(2);
 		car_(v) = *head;
 		cdr_(v) = tagptr(((cons_t*)ptr(v))+1, TAG_CONS);
-		car_(cdr_(v)) = cdr_(cdr_(v)) = NIL;
+		car_(cdr_(v)) = cdr_(cdr_(v)) = fl->NIL;
 		PUSH(v);
 		if(label != UNBOUND)
-			ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
+			ptrhash_put(&fl->readstate->backrefs, (void*)label, (void*)v);
 		v = do_read_sexpr(UNBOUND);
-		car_(cdr_(Stack[SP-1])) = v;
+		car_(cdr_(fl->Stack[fl->SP-1])) = v;
 		return POP();
 	case TOK_SHARPQUOTE:
 		// femtoLisp doesn't need symbol-function, so #' does nothing
 		return do_read_sexpr(label);
 	case TOK_OPEN:
-		PUSH(NIL);
-		read_list(&Stack[SP-1], label, TOK_CLOSE);
+		PUSH(fl->NIL);
+		read_list(&fl->Stack[fl->SP-1], label, TOK_CLOSE);
 		return POP();
 	case TOK_OPENB:
-		PUSH(NIL);
-		read_list(&Stack[SP-1], label, TOK_CLOSEB);
+		PUSH(fl->NIL);
+		read_list(&fl->Stack[fl->SP-1], label, TOK_CLOSEB);
 		return POP();
 	case TOK_OPENC:
-		PUSH(NIL);
-		read_list(&Stack[SP-1], label, TOK_CLOSEC);
+		PUSH(fl->NIL);
+		read_list(&fl->Stack[fl->SP-1], label, TOK_CLOSEC);
 		return POP();
 	case TOK_SHARPSYM:
 		sym = tokval;
-		if(sym == tsym || sym == Tsym)
-			return FL_T;
-		if(sym == fsym || sym == Fsym)
-			return FL_F;
+		if(sym == fl->tsym || sym == fl->Tsym)
+			return fl->FL_T;
+		if(sym == fl->fsym || sym == fl->Fsym)
+			return fl->FL_F;
 		// constructor notation
 		c = nextchar();
 		if(c != '('){
 			take();
-			lerrorf(ParseError, "expected argument list for %s", symbol_name(tokval));
+			lerrorf(fl->ParseError, "expected argument list for %s", symbol_name(tokval));
 		}
-		PUSH(NIL);
-		read_list(&Stack[SP-1], UNBOUND, TOK_CLOSE);
-		if(sym == vu8sym){
-			sym = arraysym;
-			Stack[SP-1] = fl_cons(uint8sym, Stack[SP-1]);
-		}else if(sym == fnsym){
-			sym = FUNCTION;
+		PUSH(fl->NIL);
+		read_list(&fl->Stack[fl->SP-1], UNBOUND, TOK_CLOSE);
+		if(sym == fl->vu8sym){
+			sym = fl->arraysym;
+			fl->Stack[fl->SP-1] = fl_cons(fl->uint8sym, fl->Stack[fl->SP-1]);
+		}else if(sym == fl->fnsym){
+			sym = fl->FUNCTION;
 		}
 		v = symbol_value(sym);
 		if(v == UNBOUND)
@@ -719,20 +719,20 @@
 		return fl_toplevel_eval(sym);
 	case TOK_LABEL:
 		// create backreference label
-		if(ptrhash_has(&readstate->backrefs, (void*)tokval))
-			lerrorf(ParseError, "label %"PRIdPTR" redefined", numval(tokval));
+		if(ptrhash_has(&fl->readstate->backrefs, (void*)tokval))
+			lerrorf(fl->ParseError, "label %"PRIdPTR" redefined", numval(tokval));
 		oldtokval = tokval;
 		v = do_read_sexpr(tokval);
-		ptrhash_put(&readstate->backrefs, (void*)oldtokval, (void*)v);
+		ptrhash_put(&fl->readstate->backrefs, (void*)oldtokval, (void*)v);
 		return v;
 	case TOK_BACKREF:
 		// look up backreference
-		v = (value_t)ptrhash_get(&readstate->backrefs, (void*)tokval);
+		v = (value_t)ptrhash_get(&fl->readstate->backrefs, (void*)tokval);
 		if(v == (value_t)HT_NOTFOUND)
-			lerrorf(ParseError, "undefined label %"PRIdPTR, numval(tokval));
+			lerrorf(fl->ParseError, "undefined label %"PRIdPTR, numval(tokval));
 		return v;
 	case TOK_GENSYM:
-		pv = (value_t*)ptrhash_bp(&readstate->gensyms, (void*)tokval);
+		pv = (value_t*)ptrhash_bp(&fl->readstate->gensyms, (void*)tokval);
 		if(*pv == (value_t)HT_NOTFOUND)
 			*pv = gensym();
 		return *pv;
@@ -739,7 +739,7 @@
 	case TOK_DOUBLEQUOTE:
 		return read_string();
 	}
-	return FL_UNSPECIFIED;
+	return fl->FL_UNSPECIFIED;
 }
 
 value_t
@@ -747,11 +747,11 @@
 {
 	value_t v;
 	fl_readstate_t state;
-	state.prev = readstate;
+	state.prev = fl->readstate;
 	htable_new(&state.backrefs, 8);
 	htable_new(&state.gensyms, 8);
 	state.source = f;
-	readstate = &state;
+	fl->readstate = &state;
 	assert(toktype == TOK_NONE);
 	fl_gc_handle(&tokval);
 
@@ -758,7 +758,7 @@
 	v = do_read_sexpr(UNBOUND);
 
 	fl_free_gc_handles(1);
-	readstate = state.prev;
+	fl->readstate = state.prev;
 	free_readstate(&state);
 	return v;
 }
--- a/sixel.c
+++ b/sixel.c
@@ -21,20 +21,18 @@
 	int bufsz;
 };
 
-static value_t fsosym;
-static fltype_t *fsotype;
 static sixel_allocator_t *salloc;
 
 static int
 issixeloutput(value_t v)
 {
-	return iscvalue(v) && cv_class(ptr(v)) == fsotype;
+	return iscvalue(v) && cv_class(ptr(v)) == fl->fsotype;
 }
 
 BUILTIN("sixel-ouput?", fsixel_outputp)
 {
 	argcount(nargs, 1);
-	return issixeloutput(args[0]) ? FL_T : FL_F;
+	return issixeloutput(args[0]) ? fl->FL_T : fl->FL_F;
 }
 
 static int
@@ -53,17 +51,17 @@
 		numcolors = toulong(args[0]);
 	}
 	if(numcolors < 1 || numcolors > 256)
-		lerrorf(ArgError, "invalid number of colors: %d", numcolors);
-	value_t v = cvalue(fsotype, sizeof(fso_t));
+		lerrorf(fl->ArgError, "invalid number of colors: %d", numcolors);
+	value_t v = cvalue(fl->fsotype, sizeof(fso_t));
 	fso_t *f = value2c(fso_t*, v);
 	if(salloc == nil)
 		sixel_allocator_new(&salloc, malloc, calloc, realloc, free);
 	SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc);
 	if(SIXEL_FAILED(r))
-		lerrorf(IOError, "could not create sixel output");
+		lerrorf(fl->IOError, "could not create sixel output");
 	r = sixel_dither_new(&f->dither, numcolors, salloc);
 	if(SIXEL_FAILED(r))
-		lerrorf(IOError, "could not create sixel dither");
+		lerrorf(fl->IOError, "could not create sixel dither");
 	sixel_output_set_palette_type(f->out, SIXEL_PALETTETYPE_RGB);
 	sixel_dither_set_pixelformat(f->dither, SIXEL_PIXELFORMAT_PAL8);
 	sixel_dither_set_transparent(f->dither, 0xff);
@@ -86,10 +84,10 @@
 	if(nargs > 2)
 		scaley = toulong(args[2]);
 	if(scalex < 1 || scalex > 32 || scaley < 1 || scaley > 32)
-		lerrorf(ArgError, "invalid scale factor: %dx%d", scalex, scaley);
+		lerrorf(fl->ArgError, "invalid scale factor: %dx%d", scalex, scaley);
 	f->scalex = scalex;
 	f->scaley = scaley;
-	return FL_T;
+	return fl->FL_T;
 }
 
 // :: sixel-output -> palette -> [paltype ->] ...
@@ -112,7 +110,7 @@
 		else if(len == 3 && strncmp(s, "hls", 3) == 0)
 			isrgb = false;
 		else
-			lerrorf(ArgError, "invalid palette type (must be either \"rgb\" or \"hls\")");
+			lerrorf(fl->ArgError, "invalid palette type (must be either \"rgb\" or \"hls\")");
 	}
 
 	if(!isarray(args[1]))
@@ -119,7 +117,7 @@
 		type_error("array", args[1]);
 	len = cvalue_arraylen(args[1]);
 	if(f->numcolors*3 != (int)len)
-		lerrorf(ArgError, "invalid palette: expected %d colors, got %d", f->numcolors, (int)len);
+		lerrorf(fl->ArgError, "invalid palette: expected %d colors, got %d", f->numcolors, (int)len);
 
 	fltype_t *type = cv_class(ptr(args[1]));
 	size_t elsize = type->elsz;
@@ -128,10 +126,10 @@
 
 	uint8_t out[256*3] = {0};
 	if(isrgb){
-		if(eltype->type == uint8sym || eltype->type == bytesym)
+		if(eltype->type == fl->uint8sym || eltype->type == fl->bytesym)
 			memcpy(out, cptr(args[1]), f->numcolors*3);
 		else
-			lerrorf(ArgError, "invalid palette type: expected bytes");
+			lerrorf(fl->ArgError, "invalid palette type: expected bytes");
 	}else{
 		uint8_t *pal = cptr(args[1]);
 		for(int i = 0; i < f->numcolors; i++){
@@ -163,7 +161,7 @@
 	}
 	sixel_dither_set_palette(f->dither, out);
 
-	return FL_T;
+	return fl->FL_T;
 }
 
 // :: sixel-output -> iostream -> pixels -> width -> height -> ...
@@ -187,7 +185,7 @@
 	if(f->scalex > 1 || f->scaley > 1){
 		int ow = w * f->scalex, oh = h * f->scaley, osz = ow*oh;
 		if(ow < 1 || oh < 1 || osz < ow || osz < oh)
-			lerrorf(ArgError, "scaling out of range");
+			lerrorf(fl->ArgError, "scaling out of range");
 		if(f->bufsz < osz){
 			f->buf = LLT_REALLOC(f->buf, osz);
 			f->bufsz = osz;
@@ -202,7 +200,7 @@
 			salloc
 		);
 		if(SIXEL_FAILED(r))
-			lerrorf(IOError, "could not scale image");
+			lerrorf(fl->IOError, "could not scale image");
 		w = ow;
 		h = oh;
 		pix = f->buf;
@@ -213,8 +211,8 @@
 	}
 	r = sixel_encode(pix, w, h, 0, f->dither, f->out);
 	if(SIXEL_FAILED(r))
-		lerrorf(IOError, "could not encode image");
-	return FL_T;
+		lerrorf(fl->IOError, "could not encode image");
+	return fl->FL_T;
 }
 
 static void
@@ -232,7 +230,7 @@
 	sixel_output_destroy(oldf->out);
 	SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc);
 	if(SIXEL_FAILED(r))
-		lerrorf(IOError, "could not recreate sixel output");
+		lerrorf(fl->IOError, "could not recreate sixel output");
 	sixel_output_set_palette_type(f->out, SIXEL_PALETTETYPE_RGB);
 }
 
@@ -255,6 +253,6 @@
 void
 fsixel_init(void)
 {
-	fsosym = symbol("sixel-output");
-	fsotype = define_opaque_type(fsosym, sizeof(fso_t), &fso_vtable, nil);
+	fl->fsosym = symbol("sixel-output");
+	fl->fsotype = define_opaque_type(fl->fsosym, sizeof(fso_t), &fso_vtable, nil);
 }
--- a/string.c
+++ b/string.c
@@ -13,7 +13,7 @@
 BUILTIN("string?", stringp)
 {
 	argcount(nargs, 1);
-	return fl_isstring(args[0]) ? FL_T : FL_F;
+	return fl_isstring(args[0]) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("string-length", string_length)
@@ -46,9 +46,9 @@
 	argcount(nargs, 1);
 	if(iscprim(args[0])){
 		cprim_t *cp = ptr(args[0]);
-		if(cp_class(cp) == runetype){
+		if(cp_class(cp) == fl->runetype){
 			int w = wcwidth(*(Rune*)cp_data(cp));
-			return w < 0 ? FL_F : fixnum(w);
+			return w < 0 ? fl->FL_F : fixnum(w);
 		}
 	}
 	return size_wrap(u8_strwidth(tostring(args[0])));
@@ -71,7 +71,7 @@
 	if(iscvalue(args[0])){
 		cvalue_t *cv = ptr(args[0]);
 		fltype_t *t = cv_class(cv);
-		if(t->eltype == runetype){
+		if(t->eltype == fl->runetype){
 			size_t nr = cv_len(cv) / sizeof(Rune);
 			Rune *r = (Rune*)cv_data(cv);
 			size_t nb = runenlen(r, nr);
@@ -89,7 +89,7 @@
 {
 	int term = 0;
 	if(nargs == 2)
-		term = args[1] != FL_F;
+		term = args[1] != fl->FL_F;
 	else
 		argcount(nargs, 1);
 	if(!fl_isstring(args[0]))
@@ -101,7 +101,7 @@
 	size_t newsz = nc*sizeof(Rune);
 	if(term)
 		newsz += sizeof(Rune);
-	value_t runestr = cvalue(runestringtype, newsz);
+	value_t runestr = cvalue(fl->runestringtype, newsz);
 	ptr = cv_data(ptr(args[0]));  // relocatable pointer
 	Rune *r = cvalue_data(runestr);
 	for(size_t i = 0; i < nb; i++)
@@ -121,16 +121,16 @@
 	fl_gc_handle(&buf);
 	ios_t *s = value2c(ios_t*, buf);
 	int i;
-	value_t oldpr = symbol_value(printreadablysym);
-	value_t oldpp = symbol_value(printprettysym);
-	set(printreadablysym, FL_F);
-	set(printprettysym, FL_F);
+	value_t oldpr = symbol_value(fl->printreadablysym);
+	value_t oldpp = symbol_value(fl->printprettysym);
+	set(fl->printreadablysym, fl->FL_F);
+	set(fl->printprettysym, fl->FL_F);
 	FOR_ARGS(i, 0, arg, args){
 		USED(arg);
 		fl_print(s, args[i]);
 	}
-	set(printreadablysym, oldpr);
-	set(printprettysym, oldpp);
+	set(fl->printreadablysym, oldpr);
+	set(fl->printprettysym, oldpp);
 	value_t outp = stream_to_string(&buf);
 	fl_free_gc_handles(1);
 	return outp;
@@ -144,7 +144,7 @@
 	size_t len = cv_len(ptr(args[0]));
 	size_t dlen = cv_len(ptr(args[1]));
 	size_t ssz, tokend, tokstart, i = 0;
-	value_t first = FL_NIL, c = FL_NIL, last;
+	value_t first = fl->FL_NIL, c = fl->FL_NIL, last;
 	size_t junk;
 	fl_gc_handle(&first);
 	fl_gc_handle(&last);
@@ -156,7 +156,7 @@
 			tokend = i;
 		ssz = tokend - tokstart;
 		last = c; // save previous cons cell
-		c = fl_cons(cvalue_string(ssz), FL_NIL);
+		c = fl_cons(cvalue_string(ssz), fl->FL_NIL);
 
 		// we've done allocation; reload movable pointers
 		s = cv_data(ptr(args[0]));
@@ -166,7 +166,7 @@
 			memmove(cv_data(ptr(car_(c))), &s[tokstart], ssz);
 
 		// link new cell
-		if(last == FL_NIL)
+		if(last == fl->FL_NIL)
 			first = c;   // first time, save first cons
 		else
 			((cons_t*)ptr(last))->cdr = c;
@@ -199,7 +199,7 @@
 			bounds_error(args[0], args[2]);
 	}
 	if(endbytes == startbytes)
-		return symbol_value(emptystringsym);
+		return symbol_value(fl->emptystringsym);
 	value_t ns = cvalue_string(endbytes-startbytes);
 	memmove(cv_data(ptr(ns)), s+startbytes, endbytes-startbytes);
 	return ns;
@@ -224,7 +224,7 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = (cprim_t*)ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
 	return mk_rune(toupperrune(*(Rune*)cp_data(cp)));
 }
@@ -233,7 +233,7 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
 	return mk_rune(tolowerrune(*(Rune*)cp_data(cp)));
 }
@@ -242,7 +242,7 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
 	return mk_rune(totitlerune(*(Rune*)cp_data(cp)));
 }
@@ -251,9 +251,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return isalpharune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return isalpharune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("char-lower-case?", char_lower_casep)
@@ -260,9 +260,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return islowerrune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return islowerrune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("char-upper-case?", char_upper_casep)
@@ -269,9 +269,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return isupperrune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return isupperrune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("char-title-case?", char_title_casep)
@@ -278,9 +278,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return istitlerune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return istitlerune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("char-numeric?", char_numericp)
@@ -287,9 +287,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return isdigitrune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return isdigitrune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("char-whitespace?", char_whitespacep)
@@ -296,9 +296,9 @@
 {
 	argcount(nargs, 1);
 	cprim_t *cp = ptr(args[0]);
-	if(!iscprim(args[0]) || cp_class(cp) != runetype)
+	if(!iscprim(args[0]) || cp_class(cp) != fl->runetype)
 		type_error("rune", args[0]);
-	return isspacerune(*(Rune*)cp_data(cp)) ? FL_T : FL_F;
+	return isspacerune(*(Rune*)cp_data(cp)) ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("string-find", string_find)
@@ -317,12 +317,12 @@
 
 	value_t v = args[1];
 	cprim_t *cp = ptr(v);
-	if(iscprim(v) && cp_class(cp) == runetype){
+	if(iscprim(v) && cp_class(cp) == fl->runetype){
 		Rune r = *(Rune*)cp_data(cp);
 		needlesz = runetochar(cbuf, &r);
 		needle = cbuf;
 		needle[needlesz] = 0;
-	}else if(iscprim(v) && cp_class(cp) == bytetype){
+	}else if(iscprim(v) && cp_class(cp) == fl->bytetype){
 		needlesz = 1;
 		needle = cbuf;
 		needle[0] = *(char*)cp_data(cp);
@@ -335,7 +335,7 @@
 		type_error("string", args[1]);
 	}
 	if(needlesz > len-start)
-		return FL_F;
+		return fl->FL_F;
 	if(needlesz == 0)
 		return size_wrap(start);
 	size_t i;
@@ -343,7 +343,7 @@
 		if(s[i] == needle[0] && memcmp(&s[i+1], needle+1, needlesz-1) == 0)
 			return size_wrap(i);
 	}
-	return FL_F;
+	return fl->FL_F;
 }
 
 static unsigned long
@@ -351,7 +351,7 @@
 {
 	unsigned long radix = toulong(arg);
 	if(radix < 2 || radix > 36)
-		lerrorf(ArgError, "invalid radix");
+		lerrorf(fl->ArgError, "invalid radix");
 	return radix;
 }
 
@@ -392,7 +392,7 @@
 	if(nargs == 2)
 		radix = get_radix_arg(args[1]);
 	if(!isnumtok_base(str, &n, (int)radix))
-		return FL_F;
+		return fl->FL_F;
 	return n;
 }
 
@@ -401,5 +401,5 @@
 	argcount(nargs, 1);
 	char *s = tostring(args[0]);
 	size_t len = cv_len((cvalue_t*)ptr(args[0]));
-	return u8_isvalid(s, len) ? FL_T : FL_F;
+	return u8_isvalid(s, len) ? fl->FL_T : fl->FL_F;
 }
--- a/table.c
+++ b/table.c
@@ -6,9 +6,6 @@
 #include "print.h"
 #include "table.h"
 
-static value_t tablesym;
-static fltype_t *tabletype;
-
 static void
 print_htable(value_t v, ios_t *f)
 {
@@ -73,13 +70,13 @@
 static int
 ishashtable(value_t v)
 {
-	return iscvalue(v) && cv_class((cvalue_t*)ptr(v)) == tabletype;
+	return iscvalue(v) && cv_class((cvalue_t*)ptr(v)) == fl->tabletype;
 }
 
 BUILTIN("table?", tablep)
 {
 	argcount(nargs, 1);
-	return ishashtable(args[0]) ? FL_T : FL_F;
+	return ishashtable(args[0]) ? fl->FL_T : fl->FL_F;
 }
 
 static htable_t *
@@ -94,17 +91,17 @@
 {
 	size_t cnt = (size_t)nargs;
 	if(cnt & 1)
-		lerrorf(ArgError, "arguments must come in pairs");
+		lerrorf(fl->ArgError, "arguments must come in pairs");
 	value_t nt;
 	// prevent small tables from being added to finalizer list
 	if(cnt <= HT_N_INLINE)
-		nt = cvalue_nofinalizer(tabletype, sizeof(htable_t));
+		nt = cvalue_nofinalizer(fl->tabletype, sizeof(htable_t));
 	else
-		nt = cvalue(tabletype, 2*sizeof(void*));
+		nt = cvalue(fl->tabletype, 2*sizeof(void*));
 	htable_t *h = (htable_t*)cv_data((cvalue_t*)ptr(nt));
 	htable_new(h, cnt/2);
 	int i;
-	value_t k = FL_NIL, arg;
+	value_t k = fl->FL_NIL, arg;
 	FOR_ARGS(i, 0, arg, args){
 		if(i & 1)
 			equalhash_put(h, (void*)k, (void*)arg);
@@ -138,7 +135,7 @@
 static void
 key_error(value_t key)
 {
-	lerrorf(fl_list2(KeyError, key), "key not found");
+	lerrorf(fl_list2(fl->KeyError, key), "key not found");
 }
 
 // (get table key [default])
@@ -161,7 +158,7 @@
 {
 	argcount(nargs, 2);
 	htable_t *h = totable(args[0]);
-	return equalhash_has(h, (void*)args[1]) ? FL_T : FL_F;
+	return equalhash_has(h, (void*)args[1]) ? fl->FL_T : fl->FL_F;
 }
 
 // (del! table key)
@@ -190,7 +187,7 @@
 			// reload pointer
 			h = (htable_t*)cv_data(ptr(t));
 			if(h->size != n)
-				lerrorf(EnumerationError, "table modified");
+				lerrorf(fl->EnumerationError, "table modified");
 			table = h->table;
 		}
 	}
@@ -201,6 +198,6 @@
 void
 table_init(void)
 {
-	tablesym = symbol("table");
-	tabletype = define_opaque_type(tablesym, sizeof(htable_t), &table_vtable, nil);
+	fl->tablesym = symbol("table");
+	fl->tabletype = define_opaque_type(fl->tablesym, sizeof(htable_t), &table_vtable, nil);
 }
--- a/terminal_posix.c
+++ b/terminal_posix.c
@@ -55,7 +55,7 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return termsetraw(true, cursorvisible) == 0 ? FL_T : FL_F;
+	return termsetraw(true, cursorvisible) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("terminal-leave-raw-mode", terminal_leave_raw_mode)
@@ -62,7 +62,7 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return termsetraw(false, cursorvisible) == 0 ? FL_T : FL_F;
+	return termsetraw(false, cursorvisible) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("terminal-show-cursor", terminal_show_cursor)
@@ -69,7 +69,7 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return termsetraw(inraw, true) == 0 ? FL_T : FL_F;
+	return termsetraw(inraw, true) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("terminal-hide-cursor", terminal_hide_cursor)
@@ -76,7 +76,7 @@
 {
 	USED(args);
 	argcount(nargs, 0);
-	return termsetraw(inraw, false) == 0 ? FL_T : FL_F;
+	return termsetraw(inraw, false) == 0 ? fl->FL_T : fl->FL_F;
 }
 
 BUILTIN("terminal-get-size", terminal_get_size)
@@ -85,11 +85,11 @@
 	argcount(nargs, 0);
 	struct winsize s;
 	if(ioctl(STDIN_FILENO, TIOCGWINSZ, &s) < 0)
-		return FL_F;
+		return fl->FL_F;
 	value_t v = mk_cons(), tex, pix;
 	car_(v) = tex = mk_cons();
 	car_(tex) = fixnum(s.ws_col);
-	cdr_(tex) = mk_cons(); car_(cdr_(tex)) = fixnum(s.ws_row); cdr_(cdr_(tex)) = NIL;
+	cdr_(tex) = mk_cons(); car_(cdr_(tex)) = fixnum(s.ws_row); cdr_(cdr_(tex)) = fl->NIL;
 	int x = s.ws_xpixel, y = s.ws_ypixel;
 	bool wasraw = inraw;
 	if((x == 0 || y == 0) && isatty(STDOUT_FILENO) && termsetraw(true, cursorvisible) == 0){
@@ -124,9 +124,9 @@
 		if(!wasraw)
 			termsetraw(false, cursorvisible);
 	}
-	cdr_(v) = pix = mk_cons(); cdr_(pix) = NIL;
+	cdr_(v) = pix = mk_cons(); cdr_(pix) = fl->NIL;
 	car_(pix) = mk_cons(); pix = car_(pix);
 	car_(pix) = fixnum(x);
-	cdr_(pix) = mk_cons(); car_(cdr_(pix)) = fixnum(y); cdr_(cdr_(pix)) = NIL;
+	cdr_(pix) = mk_cons(); car_(cdr_(pix)) = fixnum(y); cdr_(cdr_(pix)) = fl->NIL;
 	return v;
 }
--- a/types.c
+++ b/types.c
@@ -13,11 +13,11 @@
 		if(ft != nil)
 			return ft;
 	}
-	void **bp = equalhash_bp(&TypeTable, (void*)t);
+	void **bp = equalhash_bp(&fl->TypeTable, (void*)t);
 	if(*bp != HT_NOTFOUND)
 		return *bp;
 
-	int align, isarray = iscons(t) && car_(t) == arraysym && iscons(cdr_(t));
+	int align, isarray = iscons(t) && car_(t) == fl->arraysym && iscons(cdr_(t));
 	size_t sz;
 	if(isarray && !iscons(cdr_(cdr_(t)))){
 		// special case: incomplete array type
@@ -41,13 +41,13 @@
 			fltype_t *eltype = get_type(car_(cdr_(t)));
 			if(eltype->size == 0){
 				LLT_FREE(ft);
-				lerrorf(ArgError, "invalid array element type");
+				lerrorf(fl->ArgError, "invalid array element type");
 			}
 			ft->elsz = eltype->size;
 			ft->eltype = eltype;
 			ft->init = cvalue_array_init;
 			//eltype->artype = ft; -- this is a bad idea since some types carry array sizes
-		}else if(car_(t) == enumsym){
+		}else if(car_(t) == fl->enumsym){
 			ft->numtype = T_INT32;
 			ft->init = cvalue_enum_init;
 		}
@@ -61,7 +61,7 @@
 {
 	fltype_t *et = get_type(eltype);
 	if(et->artype == nil)
-		et->artype = get_type(fl_list2(arraysym, eltype));
+		et->artype = get_type(fl_list2(fl->arraysym, eltype));
 	return et->artype;
 }
 
@@ -81,7 +81,7 @@
 void
 relocate_typetable(void)
 {
-	htable_t *h = &TypeTable;
+	htable_t *h = &fl->TypeTable;
 	size_t i;
 	void *nv;
 	for(i = 0; i < h->size; i += 2){