Beginning of Lisp parser
diff --git a/include/kernel/dri/pci/pci.h b/include/kernel/dri/pci/pci.h
index 196a525..68cdd61 100644
--- a/include/kernel/dri/pci/pci.h
+++ b/include/kernel/dri/pci/pci.h
@@ -2,6 +2,12 @@
 
 #include <kint.h>
 
+enum
+{
+	PCI_CONFIG_ADDRESS = 0xCF8,
+	PCI_CONFIG_DATA = 0xCFC,
+};
+
 struct pci_config_address
 {
 	union
diff --git a/share/jmk/jmk.m4 b/share/jmk/jmk.m4
index 06ca08a..4d68477 100644
--- a/share/jmk/jmk.m4
+++ b/share/jmk/jmk.m4
@@ -74,7 +74,7 @@
 define(type,
     `ifelse($1, executable,
 `$(jmk_target): $(OBJECTS)
-status_log(CC, dollar_at)
+status_log(LD, dollar_at)
 	@$(CC) -o dollar_at $^ $(CFLAGS)',
     $1, static_lib,
 `$(jmk_target): $(OBJECTS)
diff --git a/src/lisp/.clang-format b/src/lisp/.clang-format
new file mode 100644
index 0000000..bfc16c1
--- /dev/null
+++ b/src/lisp/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: Microsoft
+
+AlignAfterOpenBracket: Align
+SpacesInSquareBrackets: true
+SpaceInEmptyBlock: true
+SpacesInConditionalStatement: true
+SpaceBeforeParens: Always
+UseTab: ForIndentation
+TabWidth: 4
+ColumnLimit: 80
diff --git a/src/lisp/Jmk b/src/lisp/Jmk
new file mode 100644
index 0000000..5fc5fc4
--- /dev/null
+++ b/src/lisp/Jmk
@@ -0,0 +1,18 @@
+init(lisp, lisp)
+
+preset(optimize)
+preset(32)
+preset(debug)
+preset(warn)
+
+archetype(c)
+
+OBJECTS = main.o lisp.o
+
+type(executable)
+
+run: lisp
+	status_log(RUN, ./lisp)
+	@./lisp
+
+finish
diff --git a/src/lisp/lisp b/src/lisp/lisp
new file mode 100755
index 0000000..5f8e81b
--- /dev/null
+++ b/src/lisp/lisp
Binary files differ
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
new file mode 100644
index 0000000..be75adf
--- /dev/null
+++ b/src/lisp/lisp.c
@@ -0,0 +1,257 @@
+#include "lisp.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MIN(a, b) (a)>(b)?(b):(a)
+
+struct alloc_list *first_a = NULL, *last_a = NULL;
+
+struct value cons (struct value car, struct value cdr)
+{
+	struct cons *c = malloc (sizeof (struct cons));
+
+	struct alloc_list *item = malloc (sizeof (struct alloc_list));
+	item->type = T_CONS;
+	item->data.cons_val = c;
+
+	if ( last_a )
+	{
+		item->prev = last_a;
+		last_a->next = item;
+		item->next = NULL;
+	}
+	else
+	{
+		item->prev = item->next = NULL;
+		first_a = last_a = item;
+	}
+
+	struct value v;
+	v.tag.type = T_CONS;
+	v.value.cons_val = c;
+
+	return v;
+}
+
+void skipws (struct istream *is)
+{
+	while ( isspace (is->peek (is)) )
+		is->get (is);
+}
+
+bool isallowedchar (char c)
+{
+	return (c >= '#' && c <= '\'') ||
+		(c >= '*' && c <= '/') ||
+		(c >= '>' && c <= '@');
+}
+
+bool issymstart (char c)
+{
+	return isalpha (c) || isallowedchar (c);
+}
+
+bool issym (char c)
+{
+	return isalpha (c) || isallowedchar (c) || isdigit (c);
+}
+
+bool readsym (struct istream *is, struct value *val)
+{
+	skipws (is);
+
+	if ( !issymstart (is->peek (is)) )
+		return false;
+
+	int size = 8;
+	char *s = malloc (size);
+
+	s[0] = is->get (is);
+
+	for ( int i = 1; ; i++ )
+	{
+		if ( issym (is->peek (is)) )
+		{
+			if ( i >= size )
+			{
+				size *= 2;
+				s = realloc (s, size);
+			}
+
+			s[i] = is->get (is);
+		}
+		else
+		{
+			s[i] = 0;
+			val->tag.type = T_SYMBOL;
+			val->tag.length = i - 1;
+			val->value.symbol_val = s;
+
+			return true;
+		}
+	}
+}
+
+bool readstr (struct istream *is, struct value *val)
+{
+	skipws (is);
+
+	if ( is->peek (is) != '"' )
+		return false;
+
+	bool escape = false;
+	int size = 8;
+	char *s = malloc (size);
+
+	(void) is->get (is);
+
+	for ( int i = 0; ; i++ )
+	{
+		if ( is->peek (is) != '"' )
+		{
+			if ( i >= size )
+			{
+				i *= 2;
+				s = realloc (s, i);
+			}
+				
+			char c = is->get (is);
+
+			if ( escape && c == 'n' )
+				c = '\n';
+			else if ( escape && c == '\\' )
+				c = '\\';
+
+			if ( c == '\\' && !escape )
+			{
+				escape = true;
+				i--; // will be incremented again, UGLY.
+			}
+			else
+			{
+				escape = false;
+				s[i] = c;
+			}
+		}
+		else
+		{
+			is->get (is);
+
+			val->tag.type = T_STRING;
+			val->tag.length = i;
+			val->value.string_val = s;
+
+			return true;
+		}
+	}
+}
+
+void printval (struct value v, int depth)
+{
+	for ( int i = 0; i < depth; i++ )
+		printf("  ");
+
+	switch (v.tag.type)
+	{
+	case T_SYMBOL:
+		printf ("'%s\n", v.value.symbol_val);
+		return;
+	case T_STRING:
+		printf ("\"%s\"\n", v.value.string_val);
+		return;
+	default:
+		printf ("<unknown %d>\n", v.tag.type);
+	}
+}
+
+bool read1 (struct istream *is, struct value *val)
+{
+	if ( readsym (is, val) )
+		return true;
+
+	if ( readstr (is, val) )
+		return true;
+
+	return false;
+}
+
+struct stristream_private
+{
+	char *val;
+	int i;
+	int length;
+};
+
+int stristream_peek (struct istream *is)
+{
+	struct stristream_private *p = is->data;
+
+	if ( p->i < p->length )
+		return p->val[ p->i ];
+	else
+		return -1;
+}
+
+int stristream_get (struct istream *is)
+{
+	struct stristream_private *p = is->data;
+
+	if ( p->i < p->length )
+		return p->val[ p->i++ ];
+	else
+		return -1;
+
+}
+
+int stristream_read (struct istream *s, char *buffer, int size)
+{
+	struct stristream_private *p = s->data;
+
+	int len = MIN (size, p->length - p->i);
+	memcpy (buffer, p->val, len);
+	return len;
+}
+
+struct istream *new_stristream (char *str, int length)
+{
+	struct istream *is = malloc (sizeof (struct istream));
+	struct stristream_private *p = malloc (sizeof (struct stristream_private));
+
+	p->val = strndup (str, length);
+	p->i = 0;
+	p->length = length;
+
+	is->data = p;
+	is->get = stristream_get;
+	is->peek = stristream_peek;
+	is->read = stristream_read;
+
+	return is;
+}
+
+void del_stristream(struct istream *stristream)
+{
+	struct stristream_private *p = stristream->data;
+	free (p->val);
+	free (p);
+	free (stristream);
+}
+
+struct istream *new_stristream_nt (char *str)
+{
+	return new_stristream(str, strlen(str));
+}
+
+bool startswith (struct istream *s, const char *pattern)
+{
+	const char *check = strdup (pattern);
+	s->read (s, check, strlen (pattern));
+
+	bool res = strcmp (check, pattern) == 0;
+	free (check);
+
+	return res;
+}
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
new file mode 100644
index 0000000..016f8e5
--- /dev/null
+++ b/src/lisp/lisp.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <stdbool.h>
+
+extern unsigned int cons_magic;
+
+enum type
+{
+	T_INT = 0,
+	T_FLOAT,
+	T_NIL,
+	T_SYMBOL,
+	T_STRING,
+	T_VECTOR,
+	T_CLASS,
+	T_CONS,
+};
+
+struct tag
+{
+	unsigned int type : 3;
+	unsigned int length : 29;
+} __attribute__ ((packed));
+
+struct cons;
+
+union value_type {
+	int int_val;
+	float float_val;
+	struct cons *cons_val;
+	char *symbol_val; // interned
+	char *string_val;
+};
+
+struct value
+{
+	struct tag tag;
+	union value_type value;
+} __attribute__ ((packed));
+
+struct cons
+{
+	int magic;
+	int marked; // must be reserved
+	struct value car, cdr;
+};
+
+struct alloc_list
+{
+	int type;
+	union value_type data;
+	struct alloc_list *next, *prev;
+};
+
+struct istream
+{
+	void *data;
+
+	// These two return -1 on error
+	int (*peek) (struct istream *s);
+	int (*get) (struct istream *s);
+
+	int (*read) (struct istream *s, char *buffer, int size);
+};
+
+bool startswith (struct istream *s, const char *pattern);
+
+bool readsym (struct istream *is, struct value *val);
+bool readstr (struct istream *is, struct value *val);
+
+struct value cons (struct value car, struct value cdr);
+bool read1 (struct istream *is, struct value *val);
+struct value read (struct istream);
+struct value readn (struct istream);
+
+void printval (struct value v, int depth);
+
+struct istream *new_stristream (char *str, int length);
+// same as above but null terminated
+struct istream *new_stristream_nt (char *str);
+void del_stristream(struct istream *stristream);
diff --git a/src/lisp/main.c b/src/lisp/main.c
new file mode 100644
index 0000000..7c8224e
--- /dev/null
+++ b/src/lisp/main.c
@@ -0,0 +1,14 @@
+#include "lisp.h"
+
+int main (int argc, char **argv)
+{
+	struct istream *is = new_stristream_nt ("abcde \"asdf dsf sdf\"");
+	struct value val;
+
+	while ( read1 (is, &val) )
+	{
+		printval (val, 0);
+	}
+
+	del_stristream (is);
+}