Remove old code and update documentation

This commit is contained in:
Justine Tunney 2021-10-08 21:11:00 -07:00
parent cbb4ecc4d5
commit 4233210a86
14 changed files with 3755 additions and 702 deletions

View file

@ -1,4 +1,5 @@
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2021 Alain Greppin
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the

View file

@ -1,70 +1,28 @@
CFLAGS ?= -g
CFLAGS += -fno-pie
LDFLAGS += -no-pie # -s -static -N
REALFLAGS = \
-Os \
-D__REAL_MODE__ \
-wrapper ./realify.sh \
-ffixed-r8 \
-ffixed-r9 \
-ffixed-r10 \
-ffixed-r11 \
-ffixed-r12 \
-ffixed-r13 \
-ffixed-r14 \
-ffixed-r15 \
-mno-red-zone \
-fcall-used-rbx \
-fno-jump-tables \
-fno-shrink-wrap \
-fno-schedule-insns2 \
-flive-range-shrinkage \
-fno-omit-frame-pointer \
-momit-leaf-frame-pointer \
-mpreferred-stack-boundary=3 \
-fno-delete-null-pointer-checks
CLEANFILES = \ CLEANFILES = \
lisp \ lisp \
lisp.o \ lisp.o \
lisp.real.o \ bestline.o \
sectorlisp.o \ sectorlisp.o \
start.o \
lisp.bin \
sectorlisp.bin \ sectorlisp.bin \
lisp.bin.dbg \
sectorlisp.bin.dbg sectorlisp.bin.dbg
.PHONY: all .PHONY: all
all: lisp \ all: lisp \
lisp.bin \
lisp.bin.dbg \
sectorlisp.bin \ sectorlisp.bin \
sectorlisp.bin.dbg sectorlisp.bin.dbg
.PHONY: clean .PHONY: clean
clean:; $(RM) $(CLEANFILES) clean:; $(RM) lisp lisp.o bestline.o sectorlisp.o sectorlisp.bin sectorlisp.bin.dbg
lisp.bin.dbg: start.o lisp.real.o lisp.lds lisp: lisp.o bestline.o
lisp: lisp.o lisp.o: lisp.c bestline.h
bestline.o: bestline.c bestline.h
start.o: start.S Makefile
lisp.o: lisp.c lisp.h Makefile
lisp.real.o: lisp.c lisp.h Makefile
sectorlisp.o: sectorlisp.S sectorlisp.o: sectorlisp.S
$(AS) -g -mtune=i386 -o $@ $< $(AS) -g -mtune=i386 -o $@ $<
sectorlisp.bin.dbg: sectorlisp.o sectorlisp.bin.dbg: sectorlisp.o
$(LD) -oformat:binary -Ttext=0x7600 -o $@ $< $(LD) -oformat:binary -Ttext=0x7600 -o $@ $<
sectorlisp.bin: sectorlisp.bin.dbg sectorlisp.bin: sectorlisp.bin.dbg
objcopy -SO binary sectorlisp.bin.dbg sectorlisp.bin objcopy -S -O binary sectorlisp.bin.dbg sectorlisp.bin
%.real.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(REALFLAGS) -c -o $@ $<
%.bin.dbg:
$(LD) $(LDFLAGS) -static -o $@ $(patsubst %.lds,-T %.lds,$^)
%.bin: %.bin.dbg
objcopy -SO binary $< $@

View file

@ -1,37 +1,71 @@
# sectorlisp # sectorlisp
sectorlisp is an effort to bootstrap John McCarthy's meta-circular sectorlisp is a 512-byte implementation of LISP that's able to bootstrap
evaluator on bare metal from a 512-byte boot sector. John McCarthy's meta-circular evaluator on bare metal.
![Yo dawg, I heard you like LISP so I put a LISP in your LISP so you can eval while you eval](bin/yodawg.png) ![Yo dawg, I heard you like LISP so I put a LISP in your LISP so you can eval while you eval](bin/yodawg.png)
## Motivations ## Overview
Much of the information about LISP online tends to focus on LISP has been described as the [Maxwell's equations of
[wild macros](http://www.paulgraham.com/onlisp.html), software](https://michaelnielsen.org/ddi/lisp-as-the-maxwells-equations-of-software/).
[JIT compilation](http://pixielang.org/), or its merits as Yet there's been very little focus to date on reducing these equations
[a better XML](http://www.defmacro.org/ramblings/lisp.html) to their simplest possible form. Even the [original LISP
as well as [a better JSON](https://stopa.io/post/265). However paper](https://people.cs.umass.edu/~emery/classes/cmpsci691st/readings/PL/LISP.pdf)
there's been comparatively little focus on the from the 1960's defines LISP with nonessential elements, e.g. `LABEL`.
[primary materials](https://people.cs.umass.edu/~emery/classes/cmpsci691st/readings/PL/LISP.pdf)
from the 1950's which emphasize the radically simple nature of This project aims to solve that by doing three things:
LISP, as best evidenced by the meta-circular evaluator above.
1. We provide a LISP implementation that's written in LISP, as a single
pure expression, using only the essential functions of the language.
See [lisp.lisp](lisp.lisp). It's the same meta-circular evaluator in
John McCarthy's paper from the 1960's, except with its bugs fixed,
dependencies included, and syntactic sugar removed.
2. We provide a readable portable C reference implementation to show how
the meta-circular evaluator can be natively bootstrapped on POSIX
conforming platforms, with a pleasant readline-like interface. See
[lisp.c](lisp.c).
2. We provide a 512-byte i8086 implementation of LISP that boots from
BIOS on personal computers. See [sectorlisp.S](sectorlisp.S). To the
best of our knowledge, this is the tiniest true LISP implementation
to date.
<p align="center"> <p align="center">
<img alt="Binary Footprint Comparison" <img alt="Binary Footprint Comparison" src="bin/footprint.png">
width="750" height="348" src="bin/footprint.png">
</p> </p>
This project aims to promote the radical simplicity of the essential ## Getting Started
elements of LISP's original design, by building the tiniest LISP machine
possible. With a binary footprint less than one kilobyte, that's capable
of running natively without dependencies on modern PCs, sectorlisp might
be the tiniest self-hosting LISP interpreter to date.
We're still far off however from reaching our goal, which is to have See [lisp.lisp](lisp.lisp) for code examples that you can copy and paste
sectorlisp be small enough to fit in the master boot record of a floppy into your LISP REPL.
disk, like [sectorforth](https://github.com/cesarblum/sectorforth). If
you can help this project reach its goal, please send us a pull request! You can run the C implementation as follows:
```sh
$ make
$ ./lisp
```
After running `make` you should see a `sectorlisp.bin` file, which is a
master boot record you can put on a flopy disk and boot from BIOS. If
you would prefer to run it in an emulator, we recommend using
[Das Blinkenlights](https://justine.lol/blinkenlights/).
```sh
curl --compressed https://justine.lol/blinkenlights/blinkenlights-latest.com >blinkenlights.com
chmod +x blinkenlights.com
./blinkenlights.com -rt sectorlisp.bin
```
Alternatively you may use QEMU as follows:
```sh
qemu-system-i386 -nographic -fda sectorlisp.bin
```
Further information may be found on [our wiki](https://github.com/jart/sectorlisp/wiki).
## Demo ## Demo

3481
bestline.c Normal file

File diff suppressed because it is too large Load diff

33
bestline.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
typedef struct bestlineCompletions {
unsigned long len;
char **cvec;
} bestlineCompletions;
typedef void(bestlineCompletionCallback)(const char *, bestlineCompletions *);
typedef char *(bestlineHintsCallback)(const char *, const char **,
const char **);
typedef void(bestlineFreeHintsCallback)(void *);
void bestlineSetCompletionCallback(bestlineCompletionCallback *);
void bestlineSetHintsCallback(bestlineHintsCallback *);
void bestlineSetFreeHintsCallback(bestlineFreeHintsCallback *);
void bestlineAddCompletion(bestlineCompletions *, const char *);
char *bestline(const char *);
char *bestlineRaw(const char *, int, int);
char *bestlineWithHistory(const char *, const char *);
int bestlineHistoryAdd(const char *);
int bestlineHistorySave(const char *);
int bestlineHistoryLoad(const char *);
void bestlineFreeCompletions(bestlineCompletions *);
void bestlineHistoryFree(void);
void bestlineClearScreen(int);
void bestlineMaskModeEnable(void);
void bestlineMaskModeDisable(void);
void bestlineDisableRawMode(void);
void bestlineFree(void *);
unsigned bestlineLowercase(unsigned);
unsigned bestlineUppercase(unsigned);
void bestlineSetXlatCallback(unsigned(*)(unsigned));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

335
lisp.c
View file

@ -16,110 +16,109 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "lisp.h" #include "bestline.h"
#define RETRO 1 // auto capitalize input #ifndef __COSMOPOLITAN__
#define DELETE 1 // allow backspace to rub out symbol #include <ctype.h>
#define QUOTES 1 // allow 'X shorthand (QUOTE X) #include <stdlib.h>
#define PROMPT 1 // show repl prompt #include <string.h>
#define WORD short #include <unistd.h>
#define WORDS 8192 #endif
#define QUOTES 1 /* allow 'X shorthand for (QUOTE X) */
#define FUNDEF 1 /* be friendly w/undefined behavior */
#define TRACE 0 /* prints Eval() arguments / result */
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § LISP Machine The LISP Challenge § LISP Machine
*/ */
#define ATOM 1 #define ATOM 0
#define CONS 0 #define CONS 1
#define NIL (ATOM | 0) #define ISATOM(x) (~(x)&1)
#define UNDEFINED (ATOM | 8) #define VALUE(x) ((x)>>1)
#define ATOM_T (ATOM | 30) #define OBJECT(t,v) ((v)<<1|(t))
#define ATOM_QUOTE (ATOM | 34)
#define ATOM_COND (ATOM | 46)
#define ATOM_ATOM (ATOM | 56)
#define ATOM_CAR (ATOM | 66)
#define ATOM_CDR (ATOM | 74)
#define ATOM_CONS (ATOM | 82)
#define ATOM_EQ (ATOM | 92)
#define ATOM_LAMBDA (ATOM | 98)
#define VALUE(x) ((x) >> 1) #define NIL OBJECT(ATOM,0)
#define ATOM_T OBJECT(ATOM,4)
#define ATOM_QUOTE OBJECT(ATOM,6)
#define ATOM_COND OBJECT(ATOM,12)
#define ATOM_ATOM OBJECT(ATOM,17)
#define ATOM_CAR OBJECT(ATOM,22)
#define ATOM_CDR OBJECT(ATOM,26)
#define ATOM_CONS OBJECT(ATOM,30)
#define ATOM_EQ OBJECT(ATOM,35)
#define ATOM_LAMBDA OBJECT(ATOM,38)
#define UNDEFINED OBJECT(ATOM,45)
struct Lisp { struct Lisp {
WORD mem[WORDS]; int mem[8192];
unsigned char syntax[256]; unsigned char syntax[256];
WORD look; int look;
WORD globals; int globals;
WORD index; int index;
char token[128]; char token[128];
char str[WORDS]; char str[8192];
}; };
_Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600, static const char kSymbols[] =
"LISP Machine too large for real mode"); "NIL\0"
"T\0"
_Alignas(char) const char kSymbols[] = "NIL\0" "QUOTE\0"
"*UNDEFINED\0" "COND\0"
"T\0" "ATOM\0"
"QUOTE\0" "CAR\0"
"COND\0" "CDR\0"
"ATOM\0" "CONS\0"
"CAR\0" "EQ\0"
"CDR\0" "LAMBDA\0"
"CONS\0" #if FUNDEF
"EQ\0" "*UNDEFINED"
"LAMBDA";
#ifdef __REAL_MODE__
static struct Lisp *const q;
#else
static struct Lisp q[1];
#endif #endif
;
static void Print(long); static struct Lisp q[1];
static WORD GetList(void);
static WORD GetObject(void); static void Print(int);
static void PrintObject(long); static int GetList(void);
static WORD Eval(WORD, WORD); static int GetObject(void);
static void PrintObject(int);
static int Eval(int, int);
static void SetupSyntax(void) { static void SetupSyntax(void) {
unsigned char *syntax = q->syntax; q->syntax[' '] = ' ';
asm("" : "+bSD"(syntax)); q->syntax['\r'] = ' ';
syntax[' '] = ' '; q->syntax['\n'] = ' ';
syntax['\r'] = ' '; q->syntax['('] = '(';
syntax['\n'] = ' '; q->syntax[')'] = ')';
syntax['('] = '('; q->syntax['.'] = '.';
syntax[')'] = ')'; q->syntax['\''] = '\'';
syntax['.'] = '.';
#if QUOTES
syntax['\''] = '\'';
#endif
} }
static void SetupBuiltins(void) { static void SetupBuiltins(void) {
CopyMemory(q->str, kSymbols, sizeof(kSymbols)); memmove(q->str, kSymbols, sizeof(kSymbols));
} }
static inline WORD Car(long x) { static inline int Car(int x) {
return PEEK_ARRAY(q, mem, VALUE(x), 0); return q->mem[VALUE(x) + 0];
} }
static inline WORD Cdr(long x) { static inline int Cdr(int x) {
return PEEK_ARRAY(q, mem, VALUE(x), 1); return q->mem[VALUE(x) + 1];
} }
static WORD Set(long i, long k, long v) { static int Set(int i, int k, int v) {
POKE_ARRAY(q, mem, VALUE(i), 0, k); q->mem[VALUE(i) + 0] = k;
POKE_ARRAY(q, mem, VALUE(i), 1, v); q->mem[VALUE(i) + 1] = v;
return i; return i;
} }
static WORD Cons(WORD car, WORD cdr) { static int Cons(int car, int cdr) {
int i, cell; int i, cell;
i = q->index; i = q->index;
POKE_ARRAY(q, mem, i, 0, car); q->mem[i + 0] = car;
POKE_ARRAY(q, mem, i, 1, cdr); q->mem[i + 1] = cdr;
q->index = i + 2; q->index = i + 2;
cell = OBJECT(CONS, i); cell = OBJECT(CONS, i);
return cell; return cell;
@ -128,120 +127,116 @@ static WORD Cons(WORD car, WORD cdr) {
static char *StpCpy(char *d, char *s) { static char *StpCpy(char *d, char *s) {
char c; char c;
do { do {
c = LODS(s); // a.k.a. c = *s++ c = *s++;
STOS(d, c); // a.k.a. *d++ = c *d++ = c;
} while (c); } while (c);
return d; return d;
} }
static WORD Intern(char *s) { static int Intern(char *s) {
int j, cx; int j, cx;
char c, *z, *t; char c, *z, *t;
z = q->str; z = q->str;
c = LODS(z); c = *z++;
while (c) { while (c) {
for (j = 0;; ++j) { for (j = 0;; ++j) {
if (c != PEEK(s, j, 0)) { if (c != s[j]) {
break; break;
} }
if (!c) { if (!c) {
return OBJECT(ATOM, z - q->str - j - 1); return OBJECT(ATOM, z - q->str - j - 1);
} }
c = LODS(z); c = *z++;
} }
while (c) c = LODS(z); while (c) c = *z++;
c = LODS(z); c = *z++;
} }
--z; --z;
StpCpy(z, s); StpCpy(z, s);
return OBJECT(ATOM, SUB((long)z, q->str)); return OBJECT(ATOM, z - q->str);
} }
static unsigned char XlatSyntax(unsigned char b) { static void PrintChar(unsigned char b) {
return PEEK_ARRAY(q, syntax, b, 0); if (write(1, &b, 1) == -1) exit(1);
} }
static void PrintString(char *s) { static void PrintString(char *s) {
char c; char c;
for (;;) { for (;;) {
if (!(c = PEEK(s, 0, 0))) break; if (!(c = s[0])) break;
PrintChar(c); PrintChar(c);
++s; ++s;
} }
} }
static int GetChar(void) { static int GetChar(void) {
int c; unsigned char b;
c = ReadChar(); static char *l, *p;
#if RETRO if (l || (l = p = bestlineWithHistory("* ", "sectorlisp"))) {
if (c >= 'a') { if (*p) {
CompilerBarrier(); b = *p++;
if (c <= 'z') c -= 'a' - 'A'; } else {
free(l);
l = p = 0;
b = '\n';
}
return b;
} else {
PrintChar('\n');
exit(0);
} }
#endif
#if DELETE
if (c == '\b') return c;
#endif
PrintChar(c);
if (c == '\r') PrintChar('\n');
return c;
} }
static void GetToken(void) { static void GetToken(void) {
char *t; char *t;
unsigned char b, x; int b, x;
b = q->look; b = q->look;
t = q->token; t = q->token;
for (;;) { for (;;) {
x = XlatSyntax(b); x = q->syntax[b];
if (x != ' ') break; if (x != ' ') break;
b = GetChar(); b = GetChar();
} }
if (x) { if (x) {
STOS(t, b); *t++ = b;
b = GetChar(); b = GetChar();
} else { } else {
while (b && !x) { while (b && !x) {
if (!DELETE || b != '\b') { *t++ = b;
STOS(t, b);
} else if (t > q->token) {
PrintString("\b \b");
if (t > q->token) --t;
}
b = GetChar(); b = GetChar();
x = XlatSyntax(b); x = q->syntax[b];
} }
} }
STOS(t, 0); *t++ = 0;
q->look = b; q->look = b;
} }
static WORD ConsumeObject(void) { static int ConsumeObject(void) {
GetToken(); GetToken();
return GetObject(); return GetObject();
} }
static WORD Cadr(long x) { static int Cadr(int x) {
return Car(Cdr(x)); // ((A B C D) (E F G) H I) → (E F G) return Car(Cdr(x)); /* ((A B C D) (E F G) H I) → (E F G) */
} }
static WORD List(long x, long y) { static int List(int x, int y) {
return Cons(x, Cons(y, NIL)); return Cons(x, Cons(y, NIL));
} }
static WORD Quote(long x) { static int Quote(int x) {
return List(ATOM_QUOTE, x); return List(ATOM_QUOTE, x);
} }
static WORD GetQuote(void) { static int GetQuote(void) {
return Quote(ConsumeObject()); return Quote(ConsumeObject());
} }
static WORD AddList(WORD x) { static int AddList(int x) {
return Cons(x, GetList()); return Cons(x, GetList());
} }
static WORD GetList(void) { static int GetList(void) {
GetToken(); GetToken();
switch (*q->token & 0xFF) { switch (*q->token & 0xFF) {
default: default:
@ -257,7 +252,7 @@ static WORD GetList(void) {
} }
} }
static WORD GetObject(void) { static int GetObject(void) {
switch (*q->token & 0xFF) { switch (*q->token & 0xFF) {
default: default:
return Intern(q->token); return Intern(q->token);
@ -270,21 +265,21 @@ static WORD GetObject(void) {
} }
} }
static WORD ReadObject(void) { static int ReadObject(void) {
q->look = GetChar(); q->look = GetChar();
GetToken(); GetToken();
return GetObject(); return GetObject();
} }
static WORD Read(void) { static int Read(void) {
return ReadObject(); return ReadObject();
} }
static void PrintAtom(long x) { static void PrintAtom(int x) {
PrintString(q->str + VALUE(x)); PrintString(q->str + VALUE(x));
} }
static void PrintList(long x) { static void PrintList(int x) {
#if QUOTES #if QUOTES
if (Car(x) == ATOM_QUOTE) { if (Car(x) == ATOM_QUOTE) {
PrintChar('\''); PrintChar('\'');
@ -307,7 +302,7 @@ static void PrintList(long x) {
PrintChar(')'); PrintChar(')');
} }
static void PrintObject(long x) { static void PrintObject(int x) {
if (ISATOM(x)) { if (ISATOM(x)) {
PrintAtom(x); PrintAtom(x);
} else { } else {
@ -315,7 +310,7 @@ static void PrintObject(long x) {
} }
} }
static void Print(long i) { static void Print(int i) {
PrintObject(i); PrintObject(i);
PrintString("\r\n"); PrintString("\r\n");
} }
@ -324,55 +319,58 @@ static void Print(long i) {
The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator
*/ */
static WORD Caar(long x) { static int Caar(int x) {
return Car(Car(x)); // ((A B C D) (E F G) H I) → A return Car(Car(x)); /* ((A B C D) (E F G) H I) → A */
} }
static WORD Cdar(long x) { static int Cdar(int x) {
return Cdr(Car(x)); // ((A B C D) (E F G) H I) → (B C D) return Cdr(Car(x)); /* ((A B C D) (E F G) H I) → (B C D) */
} }
static WORD Cadar(long x) { static int Cadar(int x) {
return Cadr(Car(x)); // ((A B C D) (E F G) H I) → B return Cadr(Car(x)); /* ((A B C D) (E F G) H I) → B */
} }
static WORD Caddr(long x) { static int Caddr(int x) {
return Cadr(Cdr(x)); // ((A B C D) (E F G) H I) → H return Cadr(Cdr(x)); /* ((A B C D) (E F G) H I) → H */
} }
static WORD Caddar(long x) { static int Caddar(int x) {
return Caddr(Car(x)); // ((A B C D) (E F G) H I) → C return Caddr(Car(x)); /* ((A B C D) (E F G) H I) → C */
} }
static WORD Evcon(long c, long a) { static int Evcon(int c, int a) {
return Eval(Caar(c), a) != NIL ? Eval(Cadar(c), a) : Evcon(Cdr(c), a); return Eval(Caar(c), a) != NIL ? Eval(Cadar(c), a) : Evcon(Cdr(c), a);
} }
static WORD Assoc(long x, long a) { static int Assoc(int x, int a) {
return a != NIL ? Caar(a) == x ? Cdar(a) : Assoc(x, Cdr(a)) : NIL; return a ? Caar(a) == x ? Cdar(a) : Assoc(x, Cdr(a)) : NIL;
} }
static WORD Pairlis(WORD x, WORD y, WORD a) { static int Pairlis(int x, int y, int a) { /* it's zip() basically */
if (x == NIL) int di, si;
return a; if (!x) return a;
WORD di = Cons(Car(x), Car(y)); di = Cons(Car(x), Car(y));
WORD si = Pairlis(Cdr(x), Cdr(y), a); si = Pairlis(Cdr(x), Cdr(y), a);
return Cons(di, si); // Tail-Modulo-Cons return Cons(di, si); /* Tail-Modulo-Cons */
} }
static WORD Evlis(WORD m, WORD a) { static int Evlis(int m, int a) {
if (m == NIL) int di, si;
return NIL; if (!m) return NIL;
WORD di = Eval(Car(m), a); di = Eval(Car(m), a);
WORD si = Evlis(Cdr(m), a); si = Evlis(Cdr(m), a);
return Cons(di, si); return Cons(di, si);
} }
static WORD Apply(WORD fn, WORD x, WORD a) { static int Apply(int fn, int x, int a) {
int t1, si, ax;
if (ISATOM(fn)) { if (ISATOM(fn)) {
switch (fn) { switch (fn) {
#if FUNDEF
case NIL: case NIL:
return UNDEFINED; return UNDEFINED;
#endif
case ATOM_CAR: case ATOM_CAR:
return Caar(x); return Caar(x);
case ATOM_CDR: case ATOM_CDR:
@ -387,22 +385,20 @@ static WORD Apply(WORD fn, WORD x, WORD a) {
return Apply(Eval(fn, a), x, a); return Apply(Eval(fn, a), x, a);
} }
} }
if (Car(fn) == ATOM_LAMBDA) { if (Car(fn) == ATOM_LAMBDA) {
WORD t1 = Cdr(fn); t1 = Cdr(fn);
WORD si = Pairlis(Car(t1), x, a); si = Pairlis(Car(t1), x, a);
WORD ax = Cadr(t1); ax = Cadr(t1);
return Eval(ax, si); return Eval(ax, si);
} }
return UNDEFINED; return UNDEFINED;
} }
static WORD Eval(WORD e, WORD a) { static int Evaluate(int e, int a) {
int ax;
if (ISATOM(e)) if (ISATOM(e))
return Assoc(e, a); return Assoc(e, a);
ax = Car(e);
WORD ax = Car(e);
if (ISATOM(ax)) { if (ISATOM(ax)) {
if (ax == ATOM_QUOTE) if (ax == ATOM_QUOTE)
return Cadr(e); return Cadr(e);
@ -411,31 +407,42 @@ static WORD Eval(WORD e, WORD a) {
if (ax == ATOM_LAMBDA) if (ax == ATOM_LAMBDA)
return e; return e;
} }
return Apply(ax, Evlis(Cdr(e), a), a); return Apply(ax, Evlis(Cdr(e), a), a);
} }
static int Eval(int e, int a) {
int ax;
#if TRACE
PrintString("> ");
PrintObject(e);
PrintString("\r\n ");
PrintObject(a);
PrintString("\r\n");
#endif
ax = Evaluate(e, a);
#if TRACE
PrintString("< ");
PrintObject(ax);
PrintString("\r\n");
#endif
return ax;
}
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § User Interface The LISP Challenge § User Interface
*/ */
void Repl(void) { void Repl(void) {
for (;;) { for (;;) {
#if PROMPT
PrintString("* ");
#endif
Print(Eval(Read(), q->globals)); Print(Eval(Read(), q->globals));
} }
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
RawMode();
SetupSyntax(); SetupSyntax();
SetupBuiltins(); SetupBuiltins();
#if PROMPT bestlineSetXlatCallback(bestlineUppercase);
PrintString("THE LISP CHALLENGE V1\r\n" PrintString("THE LISP CHALLENGE V1\r\n"
"VISIT GITHUB.COM/JART\r\n"); "VISIT GITHUB.COM/JART\r\n");
#endif
Repl(); Repl();
return 0;
} }

180
lisp.h
View file

@ -1,180 +0,0 @@
#ifndef SECTORLISP_H_
#define SECTORLISP_H_
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
/*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § Richard Stallman Math 55 Systems Integration Code
*/
#define CompilerBarrier() asm volatile("" ::: "memory")
#define ISATOM(x) /* a.k.a. !(x&1) */ \
({ \
_Bool IsAtom; \
asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \
IsAtom; \
})
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
({ \
__typeof(v) Val = (v); \
asm("shl\t%0" : "+r"(Val)); \
Val | (t); \
})
#define SUB(x, y) /* a.k.a. x-y */ \
({ \
__typeof(x) Reg = (x); \
asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \
Reg; \
})
#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c))
#define LODS(si) \
({ \
typeof(*(si)) c; \
asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \
c; \
})
static inline void *SetMemory(void *di, int al, unsigned long cx) {
asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(cx), "a"(al));
return di;
}
static inline void *CopyMemory(void *di, const void *si, unsigned long cx) {
asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(si), "2"(cx));
return di;
}
static void RawMode(void) {
#ifndef __REAL_MODE__
struct termios t;
if (ioctl(1, TCGETS, &t) != -1) {
t.c_cc[VMIN] = 1;
t.c_cc[VTIME] = 1;
t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
t.c_cflag &= ~(CSIZE | PARENB);
t.c_oflag &= ~OPOST;
t.c_cflag |= CS8;
t.c_iflag |= IUTF8;
ioctl(1, TCSETS, &t);
}
#endif
}
__attribute__((__noinline__)) static void PrintChar(long c) {
#ifdef __REAL_MODE__
asm volatile("mov\t$0x0E,%%ah\n\t"
"int\t$0x10"
: /* no outputs */
: "a"(c), "b"(7)
: "memory");
#else
static short buf;
int rc;
buf = c;
write(1, &buf, 1);
#endif
}
static int ReadChar(void) {
int c;
#ifdef __REAL_MODE__
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
c &= 0xff;
#else
static int buf;
read(0, &buf, 1);
c = buf;
#endif
return c;
}
#define PEEK_(REG, BASE, INDEX, DISP) \
({ \
__typeof(*(BASE)) Reg; \
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
asm("mov\t%c2(%1),%0" \
: REG(Reg) \
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
"m"(BASE[(INDEX) + (DISP)])); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: REG(Reg) \
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
} \
Reg; \
})
#define PEEK(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
(sizeof(*(BASE)) == 1 ? PEEK_("=Q", BASE, INDEX, DISP) \
: PEEK_("=r", BASE, INDEX, DISP))
#define PEEK_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP) \
({ \
__typeof(*(OBJECT->MEMBER)) Reg; \
if (!(OBJECT)) { \
asm("mov\t%c2(%1),%0" \
: REG(Reg) \
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: REG(Reg) \
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} \
Reg; \
})
#define PEEK_ARRAY(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
(sizeof(*(OBJECT->MEMBER)) == 1 \
? PEEK_ARRAY_("=Q", OBJECT, MEMBER, INDEX, DISP) \
: PEEK_ARRAY_("=r", OBJECT, MEMBER, INDEX, DISP))
#define POKE_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
if (!(OBJECT)) { \
asm("mov\t%1,%c3(%2)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} else { \
asm("mov\t%1,%c4(%2,%3)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} \
} while (0)
#define POKE_ARRAY(OBJECT, MEMBER, INDEX, DISP, VALUE) /* o->m[i]=v */ \
do { \
__typeof(*(OBJECT->MEMBER)) Reg; \
switch (sizeof(*(OBJECT->MEMBER))) { \
case 1: \
POKE_ARRAY_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
default: \
POKE_ARRAY_("r", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
} \
} while (0)
#endif /* SECTORLISP_H_ */

View file

@ -1,36 +0,0 @@
ENTRY(_start)
SECTIONS {
.text 0x7c00 - 0x600 : {
*(.start)
*(.text.startup)
rodata = .;
*(.rodata .rodata.*)
. = 0x1fe;
SHORT(0xaa55);
*(.text .text.*)
_etext = .;
. = ALIGN(512);
}
.bss : {
bss = .;
*(.bss .bss.*)
*(COMMON)
}
/DISCARD/ : {
*(.yoink)
*(.*)
}
}
boot = 0x7c00;
q.syntax = 8192*2;
q.look = 8192*2+256;
q.globals = 8192*2+256+2;
q.index = 8192*2+256+2+2;
q.token = 8192*2+256+2+2+2;
q.str = 8192*2+256+2+2+2+128;
v_sectors = SIZEOF(.text) / 512;

View file

@ -23,8 +23,9 @@
;; ;;
;; Listed Projects ;; Listed Projects
;; ;;
;; - 836 bytes: https://github.com/jart/sectorlisp ;; - 512 bytes: https://github.com/jart/sectorlisp
;; - 13 kilobytes: https://t3x.org/klisp/ ;; - 13 kilobytes: https://t3x.org/klisp/
;; - 47 kilobytes: https://github.com/matp/tiny-lisp
;; - 150 kilobytes: https://github.com/JeffBezanson/femtolisp ;; - 150 kilobytes: https://github.com/JeffBezanson/femtolisp
;; - Send pull request to be listed here ;; - Send pull request to be listed here
;; ;;
@ -72,6 +73,7 @@ NIL
;; CORRECT RESULT OF EXPRESSION IS STILL `A` ;; CORRECT RESULT OF EXPRESSION IS STILL `A`
;; REQUIRES CONS CAR CDR QUOTE ATOM EQ LAMBDA COND ;; REQUIRES CONS CAR CDR QUOTE ATOM EQ LAMBDA COND
;; SIMPLIFIED BUG FIXED VERSION OF JOHN MCCARTHY PAPER ;; SIMPLIFIED BUG FIXED VERSION OF JOHN MCCARTHY PAPER
;; NOTE: ((EQ (CAR E) NIL) (QUOTE *UNDEFINED)) CAN HELP
((LAMBDA (ASSOC EVCON BIND APPEND EVAL) ((LAMBDA (ASSOC EVCON BIND APPEND EVAL)
(EVAL (QUOTE ((LAMBDA (FF X) (FF X)) (EVAL (QUOTE ((LAMBDA (FF X) (FF X))
(QUOTE (LAMBDA (X) (QUOTE (LAMBDA (X)
@ -98,7 +100,6 @@ NIL
((ATOM E) (ASSOC E A)) ((ATOM E) (ASSOC E A))
((ATOM (CAR E)) ((ATOM (CAR E))
(COND (COND
((EQ (CAR E) NIL) (QUOTE *UNDEFINED))
((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E))) ((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E)))
((EQ (CAR E) (QUOTE ATOM)) (ATOM (EVAL (CAR (CDR E)) A))) ((EQ (CAR E) (QUOTE ATOM)) (ATOM (EVAL (CAR (CDR E)) A)))
((EQ (CAR E) (QUOTE EQ)) (EQ (EVAL (CAR (CDR E)) A) ((EQ (CAR E) (QUOTE EQ)) (EQ (EVAL (CAR (CDR E)) A)

View file

@ -1,177 +0,0 @@
#-*-mode:sed;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: et ft=sed ts=8 tw=8 fenc=utf-8 :vi─────────────────┘
#
# SYNOPSIS
#
# sed -i -f realify.sed foo.s
#
# OVERVIEW
#
# This converts ints and longs to shorts while preserving System V ABI
# x86_64 compatibility. This works better than gcc -m16 because we can
# avoid the ASZ and OSZ prefixes in most cases while also avoiding the
# legacy 32-bit calling conventions.
# remove comments
s/[ \t][ \t]*#.*//
s/leave\(q\|\)/leavew/
s/call\(q\|\)/callw/
s/ret\(q\|\)/retw/
s/popq\t%rbp/pop\t%bp/
s/pushq\t%rbp/push\t%bp/
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
# # preserve hardcoded stack offsets
# # bloats code size 13%
# s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
# s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
# s/ret\(q\|\)/retw\t$6/
# s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
# s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
s/, /,/g
# 32-bitify
s/rax/eax/g
s/rbx/ebx/g
s/rcx/ecx/g
s/rdx/edx/g
s/rbp/ebp/g
s/rdi/edi/g
s/rsi/esi/g
s/rsp/esp/g
# unextension
s/movswl/mov/
s/movzwl/mov/
s/movslq/mov/
s/movzlq/mov/
s/movsbl/movsbw/
# unsuffix
s/^\(\t\(fild\|fist\|fistp\|fiadd\|fisub\|fisubr\|fimul\|fidiv\|fidivr\|ficom\)\)q\t/\1\t/
s/^\(\t\(mov\|add\|adc\|cmp\|test\|lea\|sbb\|mul\|imul\|div\|idiv\|in\|out\|xor\|sub\|and\|or\|rol\|ror\|rcl\|rcr\|shl\|shr\|sal\|sar\|inc\|dec\|not\|neg\)\)l\t/\1w\t/
s/^\(\t[a-z]*\)q\t/\1w\t/
s/movsww/mov/
# remove fluff
s/mov\t%eax,%eax//
s/mov\t%ebx,%ebx//
s/mov\t%ecx,%ecx//
s/mov\t%edx,%edx//
s/mov\t%ebp,%ebp//
s/mov\t%edi,%edi//
s/mov\t%esi,%esi//
s/mov\t%esp,%esp//
# make pic absolute
s/(%rip)//
# legal real mode modrm
s/(%ebx)/(%bx)/
s/(%edi)/(%di)/
s/(%esi)/(%si)/
s/(%ebp)/(%bp)/
s/(%ebx,%esi\(,1\|\))/(%bx,%si)/
s/(%ebx,%edi\(,1\|\))/(%bx,%di)/
s/(%ebp,%esi\(,1\|\))/(%bp,%si)/
s/(%ebp,%edi\(,1\|\))/(%bp,%di)/
# we need the asz prefix
s/(%eax,%eax/(%EAX,%EAX/
s/(%eax,%ebp/(%EAX,%EBP/
s/(%eax,%ebx/(%EAX,%EBX/
s/(%eax,%ecx/(%EAX,%ECX/
s/(%eax,%edi/(%EAX,%EDI/
s/(%eax,%edx/(%EAX,%EDX/
s/(%eax,%esi/(%EAX,%ESI/
s/(%ebp,%eax/(%EBP,%EAX/
s/(%ebp,%ebp/(%EBP,%EBP/
s/(%ebp,%ebx/(%EBP,%EBX/
s/(%ebp,%ecx/(%EBP,%ECX/
s/(%ebp,%edi/(%EBP,%EDI/
s/(%ebp,%edx/(%EBP,%EDX/
s/(%ebp,%esi/(%EBP,%ESI/
s/(%ebx,%eax/(%EBX,%EAX/
s/(%ebx,%ebp/(%EBX,%EBP/
s/(%ebx,%ebx/(%EBX,%EBX/
s/(%ebx,%ecx/(%EBX,%ECX/
s/(%ebx,%edi/(%EBX,%EDI/
s/(%ebx,%edx/(%EBX,%EDX/
s/(%ebx,%esi/(%EBX,%ESI/
s/(%ecx,%eax/(%ECX,%EAX/
s/(%ecx,%ebp/(%ECX,%EBP/
s/(%ecx,%ebx/(%ECX,%EBX/
s/(%ecx,%ecx/(%ECX,%ECX/
s/(%ecx,%edi/(%ECX,%EDI/
s/(%ecx,%edx/(%ECX,%EDX/
s/(%ecx,%esi/(%ECX,%ESI/
s/(%edi,%eax/(%EDI,%EAX/
s/(%edi,%ebp/(%EDI,%EBP/
s/(%edi,%ebx/(%EDI,%EBX/
s/(%edi,%ecx/(%EDI,%ECX/
s/(%edi,%edi/(%EDI,%EDI/
s/(%edi,%edx/(%EDI,%EDX/
s/(%edi,%esi/(%EDI,%ESI/
s/(%edx,%eax/(%EDX,%EAX/
s/(%edx,%ebp/(%EDX,%EBP/
s/(%edx,%ebx/(%EDX,%EBX/
s/(%edx,%ecx/(%EDX,%ECX/
s/(%edx,%edi/(%EDX,%EDI/
s/(%edx,%edx/(%EDX,%EDX/
s/(%edx,%esi/(%EDX,%ESI/
s/(%esi,%eax/(%ESI,%EAX/
s/(%esi,%ebp/(%ESI,%EBP/
s/(%esi,%ebx/(%ESI,%EBX/
s/(%esi,%ecx/(%ESI,%ECX/
s/(%esi,%edi/(%ESI,%EDI/
s/(%esi,%edx/(%ESI,%EDX/
s/(%esi,%esi/(%ESI,%ESI/
s/(%esp,%eax/(%ESP,%EAX/
s/(%esp,%ebp/(%ESP,%EBP/
s/(%esp,%ebx/(%ESP,%EBX/
s/(%esp,%ecx/(%ESP,%ECX/
s/(%esp,%edi/(%ESP,%EDI/
s/(%esp,%edx/(%ESP,%EDX/
s/(%esp,%esi/(%ESP,%ESI/
s/(,%eax/(,%EAX/
s/(,%ebx/(,%EBX/
s/(,%ecx/(,%ECX/
s/(,%edx/(,%EDX/
s/(,%esi/(,%ESI/
s/(,%edi/(,%EDI/
s/(,%ebp/(,%EBP/
s/(%eax)/(%EAX)/
s/(%ecx)/(%ECX)/
s/(%edx)/(%EDX)/
s/(%esp)/(%ESP)/
# 16bitify
s/eax/ax/g
s/ebx/bx/g
s/ecx/cx/g
s/edx/dx/g
s/ebp/bp/g
s/edi/di/g
s/esi/si/g
s/esp/sp/g
# sigh :\
# gcc needs a flag for not using rex byte regs. workaround:
# - %dil can be avoided through copious use of STOS() macro
# - %sil can be avoided through copious use of LODS() macro
# - %bpl shouldn't be allocated due to -fno-omit-frame-pointer
# - %spl shouldn't be allocated like ever
# beyond that there's only a few cases where %dil and %sil
# need some handcoded asm() macros to workaround, for example
# if ARG1 is long and you say (ARG1 & 1) gcc will use %dil
# so just kludge it using asm("and\t$1,%0" : "+Q"(ARG1))
#s/dil/bl/g
#s/sil/bh/g
#s/spl/bl/g
#s/bpl/bh/g
# nope
s/cltq//

View file

@ -1,23 +0,0 @@
#!/bin/sh
#
# SYNOPSIS
#
# gcc -g0 -Os -wrapper realify.sh -ffixed-r{8,9,1{0,1,2,4,5}}
#
# OVERVIEW
#
# Reconfigures x86_64 compiler to emit 16-bit PC boot code.
if [ "${1##*/}" = as ]; then
for x; do
if [ "${x##*.}" = s ]; then
{
printf "\t.code16gcc"
sed -f realify.sed "$x"
} >"$x".tmp
mv -f "$x".tmp "$x"
fi
done
fi
exec "$@"

46
start.S
View file

@ -1,46 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
.section .start,"ax",@progbits
.globl _start
.code16
_start: jmp 1f # some bios scan for short jump
1: ljmp $0x600>>4,$_begin # end of bios data roundup page
_begin: push %cs # memory model cs=ds=es = 0x600
push %cs
push %cs
pop %ds
pop %es
pop %ss
mov $0x70000>>4,%sp
cld
xor %ax,%ax
xor %di,%di
mov $0x7c00-0x600,%cx
rep stosb # clears our bss memory
xchg %di,%bx # start buffer at 07c00
inc %cx # start at first sector
xor %dh,%dh # drive dl head zero
mov $0x0200+v_sectors,%ax # read sectors
int $0x13 # disk service
// 𝑠𝑙𝑖𝑑𝑒
.section .yoink
nopw main