summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/avr/ao_avr_stdio.c2
-rw-r--r--src/cc1111/ao_launch.c1
-rw-r--r--src/cc1111/ao_launch.h44
-rw-r--r--src/cc1111/ao_timer.c2
-rw-r--r--src/chaoskey-v1.0/.gitignore1
-rw-r--r--src/chaoskey-v1.0/Makefile13
-rw-r--r--src/chaoskey-v1.0/chaoskey-connector.svg274
-rw-r--r--src/chaoskey-v1.0/org.altusmetrum.ChaosKey.metainfo.xml.in46
-rw-r--r--src/cortexelf-v1/Makefile137
-rw-r--r--src/cortexelf-v1/ao_1802.c328
-rw-r--r--src/cortexelf-v1/ao_1802.h129
-rw-r--r--src/cortexelf-v1/ao_cortexelf.c291
-rw-r--r--src/cortexelf-v1/ao_flip_bits.5c24
-rw-r--r--src/cortexelf-v1/ao_hex.c36
-rw-r--r--src/cortexelf-v1/ao_hex.h21
-rw-r--r--src/cortexelf-v1/ao_lisp_os.h65
-rw-r--r--src/cortexelf-v1/ao_lisp_os_save.c53
-rw-r--r--src/cortexelf-v1/ao_pins.h265
-rw-r--r--src/cortexelf-v1/cortexelf.ld101
-rw-r--r--src/cortexelf-v1/flash-loader/Makefile8
-rw-r--r--src/cortexelf-v1/flash-loader/ao_pins.h35
-rw-r--r--src/draw/5x7.bdf3190
-rw-r--r--src/draw/Makefile4
-rw-r--r--src/draw/ao_blt.c294
-rw-r--r--src/draw/ao_copy.c75
-rw-r--r--src/draw/ao_draw.h119
-rw-r--r--src/draw/ao_draw_int.h136
-rw-r--r--src/draw/ao_font.h139
-rw-r--r--src/draw/ao_line.c314
-rw-r--r--src/draw/ao_pattern.c80
-rw-r--r--src/draw/ao_rect.c46
-rw-r--r--src/draw/ao_text.c65
-rwxr-xr-xsrc/draw/font-convert150
-rw-r--r--src/draw/line.5c389
-rw-r--r--src/drivers/ao_as1107.c105
-rw-r--r--src/drivers/ao_as1107.h59
-rw-r--r--src/drivers/ao_button.c8
-rw-r--r--src/drivers/ao_cc115l.c2
-rw-r--r--src/drivers/ao_cc1200.c8
-rw-r--r--src/drivers/ao_console.c151
-rw-r--r--src/drivers/ao_console.h24
-rw-r--r--src/drivers/ao_event.h1
-rw-r--r--src/drivers/ao_fat.c4
-rw-r--r--src/drivers/ao_lco.c2
-rw-r--r--src/drivers/ao_lco_cmd.c51
-rw-r--r--src/drivers/ao_lco_func.c8
-rw-r--r--src/drivers/ao_lco_func.h2
-rw-r--r--src/drivers/ao_lco_two.c2
-rw-r--r--src/drivers/ao_matrix.c201
-rw-r--r--src/drivers/ao_matrix.h24
-rw-r--r--src/drivers/ao_pad.c29
-rw-r--r--src/drivers/ao_pad.h5
-rw-r--r--src/drivers/ao_ps2.c419
-rw-r--r--src/drivers/ao_ps2.h220
-rw-r--r--src/drivers/ao_sdcard.c6
-rw-r--r--src/drivers/ao_vga.c366
-rw-r--r--src/drivers/ao_vga.h39
-rw-r--r--src/kernel/ao.h30
-rw-r--r--src/kernel/ao_cmd.c21
-rw-r--r--src/kernel/ao_log.h30
-rw-r--r--src/kernel/ao_log_firetwo.c149
-rw-r--r--src/kernel/ao_pyro.c3
-rw-r--r--src/kernel/ao_report.c13
-rw-r--r--src/kernel/ao_stdio.c5
-rw-r--r--src/kernel/ao_task.c6
-rw-r--r--src/kernel/ao_telemetry.c2
-rw-r--r--src/kernel/ao_telemetry.h3
-rw-r--r--src/lambdakey-v1.0/.gitignore2
-rw-r--r--src/lambdakey-v1.0/Makefile92
-rw-r--r--src/lambdakey-v1.0/ao_lambdakey.c41
-rw-r--r--src/lambdakey-v1.0/ao_lisp_os.h62
-rw-r--r--src/lambdakey-v1.0/ao_lisp_os_save.c53
-rw-r--r--src/lambdakey-v1.0/ao_pins.h57
-rw-r--r--src/lambdakey-v1.0/flash-loader/.gitignore2
-rw-r--r--src/lambdakey-v1.0/flash-loader/Makefile8
-rw-r--r--src/lambdakey-v1.0/flash-loader/ao_pins.h37
-rw-r--r--src/lambdakey-v1.0/lambda.ld117
-rw-r--r--src/lisp/.gitignore2
-rw-r--r--src/lisp/Makefile22
-rw-r--r--src/lisp/Makefile-inc22
-rw-r--r--src/lisp/Makefile-lisp4
-rw-r--r--src/lisp/ao_lisp.h793
-rw-r--r--src/lisp/ao_lisp_atom.c165
-rw-r--r--src/lisp/ao_lisp_builtin.c619
-rw-r--r--src/lisp/ao_lisp_cons.c143
-rw-r--r--src/lisp/ao_lisp_const.lisp184
-rw-r--r--src/lisp/ao_lisp_error.c102
-rw-r--r--src/lisp/ao_lisp_eval.c531
-rw-r--r--src/lisp/ao_lisp_frame.c293
-rw-r--r--src/lisp/ao_lisp_int.c22
-rw-r--r--src/lisp/ao_lisp_lambda.c196
-rw-r--r--src/lisp/ao_lisp_lex.c16
-rw-r--r--src/lisp/ao_lisp_make_const.c423
-rw-r--r--src/lisp/ao_lisp_mem.c880
-rw-r--r--src/lisp/ao_lisp_os.h53
-rw-r--r--src/lisp/ao_lisp_poly.c102
-rw-r--r--src/lisp/ao_lisp_read.c498
-rw-r--r--src/lisp/ao_lisp_read.h49
-rw-r--r--src/lisp/ao_lisp_rep.c34
-rw-r--r--src/lisp/ao_lisp_save.c76
-rw-r--r--src/lisp/ao_lisp_stack.c278
-rw-r--r--src/lisp/ao_lisp_string.c158
-rw-r--r--src/lpc/Makefile-lpc.defs9
-rw-r--r--src/lpc/ao_arch_funcs.h2
-rw-r--r--src/nucleao-32/.gitignore2
-rw-r--r--src/nucleao-32/Makefile93
-rw-r--r--src/nucleao-32/ao_nucleo.c48
-rw-r--r--src/nucleao-32/ao_pins.h65
-rw-r--r--src/nucleao-32/flash-loader/.gitignore2
-rw-r--r--src/nucleao-32/flash-loader/Makefile8
-rw-r--r--src/nucleao-32/flash-loader/ao_pins.h37
-rw-r--r--src/pnpservo-v1/Makefile72
-rw-r--r--src/pnpservo-v1/ao_pins.h59
-rw-r--r--src/pnpservo-v1/ao_pnpservo.c36
-rw-r--r--src/pnpservo-v1/flash-loader/.gitignore2
-rw-r--r--src/pnpservo-v1/flash-loader/Makefile8
-rw-r--r--src/pnpservo-v1/flash-loader/ao_pins.h37
-rw-r--r--src/pnpservo-v1/lambda.ld117
-rw-r--r--src/stm-vga/Makefile83
-rw-r--r--src/stm-vga/ao_demo.c233
-rw-r--r--src/stm-vga/ao_lisp_os.h62
-rw-r--r--src/stm-vga/ao_lisp_os_save.c53
-rw-r--r--src/stm-vga/ao_pins.h219
-rw-r--r--src/stm/Makefile.defs4
-rw-r--r--src/stm/altos-512.ld98
-rw-r--r--src/stm/ao_arch.h23
-rw-r--r--src/stm/ao_arch_funcs.h174
-rw-r--r--src/stm/ao_dma_stm.c41
-rw-r--r--src/stm/ao_exti_stm.c2
-rw-r--r--src/stm/ao_flash_stm.c10
-rw-r--r--src/stm/ao_i2c_stm.c3
-rw-r--r--src/stm/ao_serial_stm.c6
-rw-r--r--src/stm/ao_timer.c1
-rw-r--r--src/stm/ao_usb_stm.c18
-rw-r--r--src/stm/stm32l.h82
-rw-r--r--src/stmf0/Makefile-stmf0.defs7
-rw-r--r--src/stmf0/altos-raw.ld85
-rw-r--r--src/stmf0/altos.ld8
-rw-r--r--src/stmf0/ao_adc_stm.c340
-rw-r--r--src/stmf0/ao_arch.h7
-rw-r--r--src/stmf0/ao_arch_funcs.h13
-rw-r--r--src/stmf0/ao_beep_stm.c389
-rw-r--r--src/stmf0/ao_crc.h3
-rw-r--r--src/stmf0/ao_flash_stm.c13
-rw-r--r--src/stmf0/ao_interrupt.c2
-rw-r--r--src/stmf0/ao_serial_stm.c500
-rw-r--r--src/stmf0/ao_spi_stm.c6
-rw-r--r--src/stmf0/ao_spi_stm_slave.c339
-rw-r--r--src/stmf0/stm32f0.h159
-rw-r--r--src/telebt-v3.0/Makefile7
-rw-r--r--src/telebt-v3.0/ao_pins.h2
-rw-r--r--src/telebt-v3.0/ao_telebt.c3
-rw-r--r--src/teledongle-v3.0/ao_pins.h1
-rw-r--r--src/telefiretwo-v0.1/ao_pins.h1
-rw-r--r--src/telefiretwo-v0.2/ao_pins.h1
-rw-r--r--src/telefiretwo-v1.0/Makefile95
-rw-r--r--src/telefiretwo-v1.0/ao_pins.h233
-rw-r--r--src/telefiretwo-v1.0/ao_telefiretwo.c73
-rw-r--r--src/telefiretwo-v1.0/flash-loader/.gitignore2
-rw-r--r--src/telefiretwo-v1.0/flash-loader/Makefile8
-rw-r--r--src/telefiretwo-v1.0/flash-loader/ao_pins.h33
-rw-r--r--src/telegps-v0.1/ao_pins.h5
-rw-r--r--src/telegps-v0.1/ao_telegps.c12
-rw-r--r--src/telegps-v0.3/ao_pins.h1
-rw-r--r--src/telegps-v1.0/ao_pins.h1
-rw-r--r--src/telegps-v2.0/Makefile89
-rw-r--r--src/telegps-v2.0/ao_pins.h164
-rw-r--r--src/telegps-v2.0/ao_telegps.c68
-rw-r--r--src/telegps-v2.0/flash-loader/Makefile8
-rw-r--r--src/telegps-v2.0/flash-loader/ao_pins.h36
-rw-r--r--src/telelco-v0.3/ao_pins.h1
-rw-r--r--src/telelcotwo-v0.1/ao_pins.h1
-rw-r--r--src/telemega-v2.0/ao_pins.h1
-rw-r--r--src/telemetrum-v3.0/ao_pins.h1
-rw-r--r--src/telemini-v2.0/ao_pins.h4
-rw-r--r--src/telemini-v3.0/Makefile93
-rw-r--r--src/telemini-v3.0/ao_pins.h188
-rw-r--r--src/telemini-v3.0/ao_telemini.c58
-rw-r--r--src/telemini-v3.0/flash-loader/Makefile8
-rw-r--r--src/telemini-v3.0/flash-loader/ao_pins.h37
-rw-r--r--src/test/Makefile15
-rw-r--r--src/test/ao_aprs_test.c16
-rw-r--r--src/test/ao_flight_test.c4
-rw-r--r--src/test/ao_lisp_os.h59
-rw-r--r--src/test/ao_lisp_test.c134
-rw-r--r--src/test/hanoi.lisp155
186 files changed, 20030 insertions, 179 deletions
diff --git a/src/avr/ao_avr_stdio.c b/src/avr/ao_avr_stdio.c
index cca2a971..fde3c421 100644
--- a/src/avr/ao_avr_stdio.c
+++ b/src/avr/ao_avr_stdio.c
@@ -21,6 +21,7 @@
int
stdio_put(char c, FILE *stream)
{
+ (void) stream;
putchar(c);
return 0;
}
@@ -28,6 +29,7 @@ stdio_put(char c, FILE *stream)
int
stdio_get(FILE *stream)
{
+ (void) stream;
return (int) getchar() & 0xff;
}
diff --git a/src/cc1111/ao_launch.c b/src/cc1111/ao_launch.c
index 4f0a0c14..76d6d13b 100644
--- a/src/cc1111/ao_launch.c
+++ b/src/cc1111/ao_launch.c
@@ -17,6 +17,7 @@
*/
#include <ao.h>
+#include <ao_launch.h>
#include <ao_radio_cmac.h>
__xdata uint16_t ao_launch_ignite;
diff --git a/src/cc1111/ao_launch.h b/src/cc1111/ao_launch.h
new file mode 100644
index 00000000..966b5cea
--- /dev/null
+++ b/src/cc1111/ao_launch.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_LAUNCH_H_
+#define _AO_LAUNCH_H_
+/* ao_launch.c */
+
+struct ao_launch_command {
+ uint16_t tick;
+ uint16_t serial;
+ uint8_t cmd;
+ uint8_t channel;
+ uint16_t unused;
+};
+
+#define AO_LAUNCH_QUERY 1
+
+struct ao_launch_query {
+ uint16_t tick;
+ uint16_t serial;
+ uint8_t channel;
+ uint8_t valid;
+ uint8_t arm_status;
+ uint8_t igniter_status;
+};
+
+#define AO_LAUNCH_ARM 2
+#define AO_LAUNCH_FIRE 3
+
+void
+ao_launch_init(void);
+
+#endif /* _AO_LAUNCH_H_ */
diff --git a/src/cc1111/ao_timer.c b/src/cc1111/ao_timer.c
index 0acef562..a3d454da 100644
--- a/src/cc1111/ao_timer.c
+++ b/src/cc1111/ao_timer.c
@@ -62,7 +62,7 @@ ao_timer_init(void)
/* NOTE: This uses a timer only present on cc1111 architecture. */
/* disable timer 1 */
- T1CTL = 0;
+/* T1CTL = 0; */
/* set the sample rate */
T1CC0H = T1_SAMPLE_TIME >> 8;
diff --git a/src/chaoskey-v1.0/.gitignore b/src/chaoskey-v1.0/.gitignore
index b0adba26..9fd59154 100644
--- a/src/chaoskey-v1.0/.gitignore
+++ b/src/chaoskey-v1.0/.gitignore
@@ -1,2 +1,3 @@
ao_product.h
chaoskey-*
+*.cab
diff --git a/src/chaoskey-v1.0/Makefile b/src/chaoskey-v1.0/Makefile
index d9944a12..f2c168ba 100644
--- a/src/chaoskey-v1.0/Makefile
+++ b/src/chaoskey-v1.0/Makefile
@@ -14,6 +14,7 @@ INC = \
ao_task.h \
ao_adc_fast.h \
ao_power.h \
+ ao_crc.h \
stm32f0.h
#
@@ -38,6 +39,8 @@ ALTOS_SRC = \
ao_gpio.c \
ao_product.c
+VENDOR=AltusMetrum
+PROJECT_NAME=ChaosKey
PRODUCT=ChaosKey-hw-1.0-sw-$(VERSION)
PRODUCT_DEF=-DCHAOSKEY_V_1_0
IDVENDOR=0x1d50
@@ -48,6 +51,7 @@ CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
PROGNAME=chaoskey-v1.0
PROG=$(PROGNAME)-$(VERSION).elf
HEX=$(PROGNAME)-$(VERSION).ihx
+METAINFO=org.altusmetrum.ChaosKey.metainfo.xml
SRC=$(ALTOS_SRC) ao_chaoskey.c
OBJ=$(SRC:.c=.o)
@@ -62,11 +66,20 @@ ao_product.h: ao-make-product.5c ../Version
$(OBJ): $(INC)
+%.cab: $(PROG) $(HEX) $(METAINFO)
+ gcab --create --nopath $@ $(PROG) $(HEX) $(METAINFO)
+
+cab: $(VENDOR)-$(PROJECT_NAME)-$(VERSION).cab
+
+check: $(METAINFO)
+ appstream-util validate-relax $(METAINFO)
+
distclean: clean
clean:
rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
rm -f ao_product.h
+ rm -f *.cab
install:
diff --git a/src/chaoskey-v1.0/chaoskey-connector.svg b/src/chaoskey-v1.0/chaoskey-connector.svg
new file mode 100644
index 00000000..671a46bd
--- /dev/null
+++ b/src/chaoskey-v1.0/chaoskey-connector.svg
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg7384"
+ sodipodi:docname="chaoskey.svg"
+ version="1.1"
+ inkscape:version="0.92pre3 r"
+ height="225"
+ width="400"
+ viewBox="0 0 400 225">
+ <metadata
+ id="metadata90">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>Gnome Symbolic Icon Theme</dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ inkscape:cy="43.99517"
+ pagecolor="#e5e6e4"
+ borderopacity="1"
+ showborder="true"
+ inkscape:bbox-paths="false"
+ guidetolerance="10"
+ inkscape:object-paths="false"
+ inkscape:window-width="2560"
+ showguides="true"
+ inkscape:object-nodes="true"
+ inkscape:snap-bbox="true"
+ inkscape:pageshadow="2"
+ inkscape:guide-bbox="true"
+ inkscape:snap-nodes="false"
+ bordercolor="#666666"
+ objecttolerance="10"
+ id="namedview88"
+ showgrid="false"
+ inkscape:window-maximized="1"
+ inkscape:window-x="2560"
+ inkscape:snap-global="true"
+ inkscape:window-y="0"
+ gridtolerance="10"
+ inkscape:window-height="1403"
+ inkscape:snap-others="false"
+ inkscape:snap-to-guides="true"
+ inkscape:current-layer="g9377"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:zoom="5.6568542"
+ inkscape:cx="79.698472"
+ inkscape:snap-grids="true"
+ inkscape:pageopacity="1"
+ inkscape:showpageshadow="false">
+ <inkscape:grid
+ spacingx="1px"
+ spacingy="1px"
+ id="grid4866"
+ empspacing="2"
+ enabled="true"
+ type="xygrid"
+ snapvisiblegridlinesonly="true"
+ visible="true" />
+ </sodipodi:namedview>
+ <title
+ id="title9167">Gnome Symbolic Icon Theme</title>
+ <defs
+ id="defs7386" />
+ <g
+ inkscape:label="figures"
+ transform="translate(-569.10098,-638)"
+ inkscape:groupmode="layer"
+ id="layer12"
+ style="display:inline">
+ <g
+ id="g9377"
+ transform="translate(6,740)">
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 643.97547,-41.91406 c -6.36396,0 -12.97215,4.683215 -12.97215,12.019445 v 70.902784 9.335743 c 0,7.247844 4.39329,11.492028 10.40889,11.492028 h 109.67315 v -0.043 H 898.3666 v -5.457 -86.88477 -5.45703 H 751.08536 v -1.3125 -4.5957 z m 143.71145,20.37304 c 6.09267,0 11.17907,4.22094 12.44336,9.85938 -1.26429,5.63804 -6.35069,9.85742 -12.44336,9.85742 -6.09267,0 -11.17752,-4.21938 -12.44141,-9.85742 1.26389,-5.63844 6.34874,-9.85938 12.44141,-9.85938 z m 0,50.5918 c 6.09267,-10e-6 11.17907,4.22094 12.44336,9.85938 -1.26429,5.63804 -6.35069,9.85742 -12.44336,9.85742 -6.09267,0 -11.17752,-4.21938 -12.44141,-9.85742 1.26389,-5.63844 6.34874,-9.85939 12.44141,-9.85938 z"
+ id="path9437"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sccsscccccccccsscscsccscc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path9406"
+ d="m 749.75794,-30.548211 v 92.340426 h 148.60837 v -92.340426 z m 37.92955,9.007314 c 7.04304,-10e-7 12.75289,5.635711 12.75289,12.5873068 0,6.9515972 -5.70985,12.586021 -12.75289,12.586021 -7.04304,0 -12.75289,-5.6344238 -12.75289,-12.586021 0,-6.9515958 5.70985,-12.5873078 12.75289,-12.5873068 z m 0,50.591426 c 7.04304,-7e-6 12.75289,5.635708 12.75289,12.587306 0,6.951598 -5.70985,12.58602 -12.75289,12.58602 -7.04304,0 -12.75289,-5.634422 -12.75289,-12.58602 0,-6.951598 5.70985,-12.587313 12.75289,-12.587306 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 749.75794,-36.005319 v 92.340426 h 148.60837 v -92.340426 z m 37.92955,9.007314 c 7.04304,-1e-6 12.75289,5.635711 12.75289,12.587308 0,6.9515972 -5.70985,12.5860213 -12.75289,12.5860213 -7.04304,0 -12.75289,-5.6344241 -12.75289,-12.5860213 0,-6.951597 5.70985,-12.587309 12.75289,-12.587308 z m 0,50.591426 c 7.04304,-7e-6 12.75289,5.635708 12.75289,12.587306 0,6.951598 -5.70985,12.58602 -12.75289,12.58602 -7.04304,0 -12.75289,-5.634422 -12.75289,-12.58602 0,-6.951598 5.70985,-12.587313 12.75289,-12.587306 z"
+ id="rect9282"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9374"
+ width="12.092357"
+ height="12.060186"
+ x="829.53729"
+ y="-29.215614"
+ rx="0"
+ ry="0" />
+ <ellipse
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="path9300"
+ cx="835.67212"
+ cy="-23.143618"
+ rx="3.6488662"
+ ry="3.6276596" />
+ <ellipse
+ cy="-11.930851"
+ cx="835.67212"
+ id="circle9316"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ rx="3.6488662"
+ ry="3.6276596" />
+ <ellipse
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="circle9318"
+ cx="835.67212"
+ cy="-1.3776593"
+ rx="3.6488662"
+ ry="3.6276596" />
+ <ellipse
+ cy="9.1755333"
+ cx="835.67212"
+ id="circle9320"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ rx="3.6488662"
+ ry="3.6276596" />
+ <ellipse
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="circle9322"
+ cx="835.67212"
+ cy="19.728724"
+ rx="3.6488662"
+ ry="3.6276596" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9339"
+ width="9.288023"
+ height="16.489361"
+ x="791.55402"
+ y="4.888298" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9341"
+ width="18.576046"
+ height="11.87234"
+ x="805.48608"
+ y="-24.132978" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9343"
+ width="17.249186"
+ height="7.9148936"
+ x="750.42133"
+ y="-18.835106" />
+ <rect
+ y="-1.6861706"
+ x="750.42133"
+ height="7.9148936"
+ width="17.249186"
+ id="rect9345"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9347"
+ width="17.249186"
+ height="7.9148936"
+ x="750.42133"
+ y="15.462767" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path9404"
+ d="m 634.00914,-35.108991 -3.0052,84.392953 c 0,8.662058 3.50963,12.551145 11.82333,12.551145 l 108.25753,0 V -37.3187 Z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ y="32.611702"
+ x="750.42133"
+ height="7.9148936"
+ width="17.249186"
+ id="rect9349"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="-24.132978"
+ x="853.25305"
+ height="38.255318"
+ width="37.815521"
+ id="rect9351"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="34.569149"
+ x="869.83881"
+ height="11.87234"
+ width="18.576046"
+ id="rect9353"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9355"
+ width="13.268604"
+ height="19.787233"
+ x="849.93591"
+ y="26.654255" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9359"
+ width="18.576046"
+ height="15.829787"
+ x="808.80322"
+ y="30.611702" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 631.00394,-29.80569 v 69.648156 c 0,6.815994 3.28909,11.242641 9.52371,11.242641 H 751.0848 V -41.914894 H 645.03698 c -7.86656,0 -14.03304,4.772971 -14.03304,12.109204 z m 35.80579,-0.962137 h 11.98062 c 1.47016,0 2.65372,1.176681 2.65372,2.638298 v 17.280335 c 0,1.4616169 -1.18356,2.6382978 -2.65372,2.6382978 h -11.98062 c -1.47016,0 -2.65372,-1.1766809 -2.65372,-2.6382978 v -17.280335 c 0,-1.461617 1.18356,-2.638298 2.65372,-2.638298 z m 0,46.829786 h 11.98062 c 1.47016,0 2.65372,1.176681 2.65372,2.638298 v 17.280337 c 0,1.461617 -1.18356,2.638298 -2.65372,2.638298 h -11.98062 c -1.47016,0 -2.65372,-1.176681 -2.65372,-2.638298 V 18.700257 c 0,-1.461617 1.18356,-2.638298 2.65372,-2.638298 z"
+ id="rect9362"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccssssssssssssssssss" />
+ <ellipse
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="path9364"
+ cx="738.81134"
+ cy="5.2446804"
+ rx="4.3122964"
+ ry="4.2872338" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9408"
+ width="37.815521"
+ height="38.255318"
+ x="853.25305"
+ y="-28.132978" />
+ <rect
+ y="26.611702"
+ x="808.80322"
+ height="15.829787"
+ width="18.576046"
+ id="rect9410"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="-28.132978"
+ x="805.48608"
+ height="11.87234"
+ width="18.576046"
+ id="rect9412"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="22.654255"
+ x="849.93591"
+ height="19.787233"
+ width="13.268604"
+ id="rect9414"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect9416"
+ width="18.576046"
+ height="11.87234"
+ x="869.83881"
+ y="30.569149" />
+ </g>
+ </g>
+</svg>
diff --git a/src/chaoskey-v1.0/org.altusmetrum.ChaosKey.metainfo.xml.in b/src/chaoskey-v1.0/org.altusmetrum.ChaosKey.metainfo.xml.in
new file mode 100644
index 00000000..6e391878
--- /dev/null
+++ b/src/chaoskey-v1.0/org.altusmetrum.ChaosKey.metainfo.xml.in
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2017 Richard Hughes <richard@hughsie.com> -->
+<component type="firmware">
+ <id>org.altusmetrum.ChaosKey.firmware</id>
+ <name>ChaosKey</name>
+ <summary>Firmware for the Altus Metrum ChaosKey</summary>
+ <description>
+ <p>
+ Updating the firmware on your ChaosKey device improves performance and adds
+ new features.
+ </p>
+ </description>
+ <provides>
+ <!-- USB\VID_1D50&PID_60C6 -->
+ <firmware type="flashed">b62500d7-c981-595b-a798-eb6cf4d4942b</firmware>
+ </provides>
+ <url type="homepage">https://chaoskey.org/</url>
+ <metadata_license>CC-BY-4.0</metadata_license>
+ <project_license>GPL-2.0</project_license>
+ <developer_name>AltusMetrum</developer_name>
+ <releases>
+ <release urgency="medium" version="@VERSION@" date="@RELEASE_DATE@">
+ <checksum filename="chaoskey-v1.0-@VERSION@.ihx" target="content"/>
+ <description>
+ <p>
+ FIXME before release.
+ </p>
+ </description>
+ </release>
+ <release urgency="medium" version="1.6.7" date="2017-01-01">
+ <checksum filename="chaoskey-v1.0-1.6.7.ihx" target="content"/>
+ <description>
+ <p>
+ Change the ADC clock speed to eliminate sampling problems which
+ cleans up the chaoskey raw data.
+ </p>
+ </description>
+ </release>
+ </releases>
+ <screenshots>
+ <screenshot type="default">
+ <image type="source">https://chaoskey.org/chaoskey-connector.svg</image>
+ <caption>Remove the plastic cover, then connect pins 1 and 5 whilst inserting into a USB socket.</caption>
+ </screenshot>
+ </screenshots>
+</component>
diff --git a/src/cortexelf-v1/Makefile b/src/cortexelf-v1/Makefile
new file mode 100644
index 00000000..8cc6ce31
--- /dev/null
+++ b/src/cortexelf-v1/Makefile
@@ -0,0 +1,137 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+LDFLAGS=-L../stm -Wl,-Tcortexelf.ld
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_kalman.h \
+ ao_product.h \
+ ao_profile.h \
+ ao_task.h \
+ math.h \
+ ao_mpu.h \
+ stm32l.h \
+ math.h \
+ ao_vga.h \
+ ao_draw.h \
+ ao_draw_int.h \
+ ao_font.h \
+ ao_ps2.h \
+ ao_lisp.h \
+ ao_lisp_const.h \
+ ao_lisp_os.h \
+ ao_flip_bits.h \
+ Makefile
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_serial_stm.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_i2c_stm.c \
+ ao_as1107.c \
+ ao_matrix.c \
+ ao_vga.c \
+ ao_blt.c \
+ ao_copy.c \
+ ao_rect.c \
+ ao_text.c \
+ ao_line.c \
+ ao_ps2.c \
+ ao_console.c \
+ ao_sdcard.c \
+ ao_bufio.c \
+ ao_fat.c \
+ ao_flash_stm.c \
+ ao_button.c \
+ ao_event.c \
+ ao_1802.c \
+ ao_hex.c \
+ ao_lisp_lex.c \
+ ao_lisp_mem.c \
+ ao_lisp_cons.c \
+ ao_lisp_eval.c \
+ ao_lisp_string.c \
+ ao_lisp_atom.c \
+ ao_lisp_int.c \
+ ao_lisp_poly.c \
+ ao_lisp_builtin.c \
+ ao_lisp_read.c \
+ ao_lisp_rep.c \
+ ao_lisp_frame.c \
+ ao_lisp_error.c \
+ ao_lisp_lambda.c \
+ ao_lisp_save.c \
+ ao_lisp_stack.c \
+ ao_lisp_os_save.c \
+ $(PROFILE) \
+ $(SAMPLE_PROFILE) \
+ $(STACK_GUARD)
+
+PRODUCT=CortexELF-v1
+PRODUCT_DEF=-DCORTEXELF
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=cortexelf-v1
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_cortexelf.c
+OBJ=$(SRC:.c=.o)
+
+all:: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) cortexelf.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+../altitude-pa.h: make-altitude-pa
+ nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean::
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h ao_flip_bits.h
+
+ao_flip_bits.h: ao_flip_bits.5c
+ nickle ao_flip_bits.5c > $@
+
+include ../lisp/Makefile-lisp
+
+install:
+
+uninstall:
diff --git a/src/cortexelf-v1/ao_1802.c b/src/cortexelf-v1/ao_1802.c
new file mode 100644
index 00000000..9fb36595
--- /dev/null
+++ b/src/cortexelf-v1/ao_1802.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_flip_bits.h>
+#include <ao_1802.h>
+#include <ao_exti.h>
+
+/* Decoded address driven by TPA/TPB signals */
+uint16_t ADDRESS;
+
+/* Decoded data, driven by TPB signal */
+uint8_t DATA;
+
+/* Mux control */
+#define _MUX_1802 0
+#define _MUX_STM 1
+
+uint8_t MUX_CONTROL;
+
+/* Signals muxed between 1802 and STM */
+uint8_t
+MRD(void) {
+ return ao_gpio_get(MRD_PORT, MRD_BIT, MRD_PIN);
+}
+
+void
+MRD_set(uint8_t value) {
+ ao_gpio_set(MRD_PORT, MRD_BIT, MRD_PIN, value);
+}
+
+uint8_t
+MWR(void) {
+ return ao_gpio_get(MWR_PORT, MWR_BIT, MWR_PIN);
+}
+
+void
+MWR_set(uint8_t value) {
+ ao_gpio_set(MWR_PORT, MWR_BIT, MWR_PIN, value);
+}
+
+static void
+TPA_rising(void)
+{
+ ADDRESS = (ADDRESS & 0x00ff) | ((uint16_t) MA() << 8);
+ ao_wakeup(&ADDRESS);
+}
+
+uint8_t
+TPA(void) {
+ return ao_gpio_get(TPA_PORT, TPA_BIT, TPA_PIN);
+}
+
+void
+TPA_set(uint8_t tpa) {
+ ao_gpio_set(TPA_PORT, TPA_BIT, TPA_PIN, tpa);
+ if (tpa)
+ TPA_rising();
+}
+
+static void
+TPB_rising(void)
+{
+ ADDRESS = (ADDRESS & 0xff00) | MA();
+ if (MWR() == 0 || MRD() == 0)
+ DATA = BUS();
+ ao_wakeup(&ADDRESS);
+}
+
+static void
+TPB_falling(void)
+{
+}
+
+uint8_t
+TPB(void) {
+ return ao_gpio_get(TPB_PORT, TPB_BIT, TPB_PIN);
+}
+
+void
+TPB_set(uint8_t tpb) {
+ ao_gpio_set(TPB_PORT, TPB_BIT, TPB_PIN, tpb);
+ if (tpb)
+ TPB_rising();
+ else
+ TPB_falling();
+}
+
+uint8_t
+MA(void) {
+ return (ao_gpio_get_all(MA_PORT) >> MA_SHIFT) & MA_MASK;
+}
+
+void
+MA_set(uint8_t ma) {
+ ao_gpio_set_mask(MA_PORT, ((uint16_t) ma) << MA_SHIFT, MA_MASK << MA_SHIFT);
+}
+
+/* Tri-state data bus */
+
+uint8_t
+BUS(void) {
+ return ao_flip_bits_8[(ao_gpio_get_all(BUS_PORT) >> BUS_SHIFT) & BUS_MASK];
+}
+
+void
+BUS_set(uint8_t bus) {
+ ao_gpio_set_mask(BUS_PORT, ao_flip_bits_8[bus] << BUS_SHIFT, BUS_MASK << BUS_SHIFT);
+}
+
+void
+BUS_stm(void)
+{
+ ao_set_output_mask(BUS_PORT, BUS_MASK << BUS_SHIFT);
+}
+
+void
+BUS_1802(void)
+{
+ ao_set_input_mask(BUS_PORT, BUS_MASK << BUS_SHIFT);
+}
+
+/* Pins controlled by 1802 */
+uint8_t
+SC(void) {
+ return ao_flip_bits_2[(ao_gpio_get_all(SC_PORT) >> SC_SHIFT) & SC_MASK];
+}
+
+uint8_t
+Q(void) {
+ return ao_gpio_get(Q_PORT, Q_BIT, Q_PIN);
+}
+
+uint8_t
+N(void) {
+ return (ao_gpio_get_all(N_PORT) >> N_SHIFT) & N_MASK;
+}
+
+/* Pins controlled by STM */
+uint8_t
+EF(void) {
+ return (ao_gpio_get_all(EF_PORT) >> EF_SHIFT) & EF_MASK;
+}
+
+void
+EF_set(uint8_t ef) {
+ ao_gpio_set_mask(EF_PORT, ef << EF_SHIFT, EF_MASK << EF_SHIFT);
+}
+
+uint8_t
+DMA_IN(void) {
+ return ao_gpio_get(DMA_IN_PORT, DMA_IN_BIT, DMA_IN_PIN);
+}
+
+void
+DMA_IN_set(uint8_t dma_in) {
+ ao_gpio_set(DMA_IN_PORT, DMA_IN_BIT, DMA_IN_PIN, dma_in);
+}
+
+uint8_t
+DMA_OUT(void) {
+ return ao_gpio_get(DMA_OUT_PORT, DMA_OUT_BIT, DMA_OUT_PIN);
+}
+
+void
+DMA_OUT_set(uint8_t dma_out) {
+ ao_gpio_set(DMA_OUT_PORT, DMA_OUT_BIT, DMA_OUT_PIN, dma_out);
+}
+
+uint8_t
+INT(void) {
+ return ao_gpio_get(INT_PORT, INT_BIT, INT_PIN);
+}
+
+void
+INT_set(uint8_t dma_out) {
+ ao_gpio_set(INT_PORT, INT_BIT, INT_PIN, dma_out);
+}
+
+uint8_t
+CLEAR(void) {
+ return ao_gpio_get(CLEAR_PORT, CLEAR_BIT, CLEAR_PIN);
+}
+
+void
+CLEAR_set(uint8_t dma_out) {
+ ao_gpio_set(CLEAR_PORT, CLEAR_BIT, CLEAR_PIN, dma_out);
+}
+
+uint8_t
+WAIT(void) {
+ return ao_gpio_get(WAIT_PORT, WAIT_BIT, WAIT_PIN);
+}
+
+void
+WAIT_set(uint8_t dma_out) {
+ ao_gpio_set(WAIT_PORT, WAIT_BIT, WAIT_PIN, dma_out);
+}
+
+void
+tpb_isr(void) {
+ /* Latch low address and data on rising edge of TPB */
+ if (TPB())
+ TPB_rising();
+ else
+ TPB_falling();
+}
+
+void
+tpa_isr(void) {
+ /* Latch high address on rising edge of TPA */
+ if (TPA())
+ TPA_rising();
+}
+
+#define ao_1802_in(port, bit, mode) do { \
+ ao_gpio_set_mode(port, bit, mode); \
+ ao_set_input(port, bit); \
+ } while (0)
+
+#define ao_1802_in_isr(port, bit, mode) do { \
+ ao_gpio_set_mode(port, bit, mode); \
+ ao_set_input(port, bit); \
+ ao_exti_enable(port, bit); \
+ } while (0)
+
+#define ao_1802_out_isr(port, bit) do { \
+ ao_exti_disable(port, bit); \
+ ao_set_output(port, bit); \
+ } while (0)
+
+void
+MUX_1802(void)
+{
+ if (MUX_CONTROL != _MUX_1802) {
+ /* Set pins to input, but pulled to idle value */
+ ao_1802_in(MRD_PORT, MRD_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_1802_in(MWR_PORT, MWR_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_1802_in_isr(TPB_PORT, TPB_BIT, AO_EXTI_MODE_PULL_DOWN);
+ ao_1802_in_isr(TPA_PORT, TPA_BIT, AO_EXTI_MODE_PULL_DOWN);
+ ao_set_input_mask(MA_PORT, MA_MASK << MA_SHIFT);
+
+ ao_gpio_set(MUX_PORT, MUX_BIT, MUX_PIN, 0);
+
+ /* Now change the pins to eliminate the pull up/down */
+ ao_gpio_set_mode(MRD_PORT, MRD_BIT, 0);
+ ao_gpio_set_mode(MWR_PORT, MWR_BIT, 0);
+ ao_gpio_set_mode(TPB_PORT, TPB_BIT, 0);
+ ao_gpio_set_mode(TPA_PORT, TPA_BIT, 0);
+
+ MUX_CONTROL = _MUX_1802;
+ }
+}
+
+void
+MUX_stm(void)
+{
+ if (MUX_CONTROL != _MUX_STM) {
+ /* Set the pins back to pull to the idle value */
+ ao_gpio_set_mode(MRD_PORT, MRD_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_gpio_set_mode(MWR_PORT, MWR_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_gpio_set_mode(TPB_PORT, TPB_BIT, AO_EXTI_MODE_PULL_DOWN);
+ ao_gpio_set_mode(TPA_PORT, TPA_BIT, AO_EXTI_MODE_PULL_DOWN);
+
+ ao_gpio_set(MUX_PORT, MUX_BIT, MUX_PIN, 1);
+
+ /* Now set the pins as output, driven to the idle value */
+ ao_set_output(MRD_PORT, MRD_BIT, MRD_PIN, 1);
+ ao_set_output(MWR_PORT, MWR_BIT, MWR_PIN, 1);
+ ao_set_output(TPB_PORT, TPB_BIT, TPB_PIN, 0);
+ ao_set_output(TPA_PORT, TPA_BIT, TPA_PIN, 0);
+ ao_set_output_mask(MA_PORT, MA_MASK << MA_SHIFT);
+ MUX_CONTROL = _MUX_STM;
+ }
+}
+
+void
+ao_1802_init(void)
+{
+ /* Multiplexed signals*/
+
+ /* active low signals */
+ ao_enable_input(MRD_PORT, MRD_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_enable_input(MWR_PORT, MWR_BIT, AO_EXTI_MODE_PULL_UP);
+
+ /* active high signals with interrupts */
+ ao_exti_setup(TPA_PORT, TPA_BIT,
+ AO_EXTI_MODE_PULL_DOWN | AO_EXTI_MODE_RISING | AO_EXTI_MODE_FALLING,
+ tpa_isr);
+ ao_exti_setup(TPB_PORT, TPB_BIT,
+ AO_EXTI_MODE_PULL_DOWN | AO_EXTI_MODE_RISING | AO_EXTI_MODE_FALLING,
+ tpb_isr);
+
+ /* multiplexed address bus */
+ ao_enable_input_mask(MA_PORT, MA_MASK << MA_SHIFT, 0);
+
+ /* Data bus */
+
+ ao_enable_input_mask(BUS_PORT, BUS_MASK << BUS_SHIFT, 0);
+
+ /* Pins controlled by 1802 */
+ ao_enable_input_mask(SC_PORT, SC_MASK << SC_SHIFT, 0);
+ ao_enable_input(Q_PORT, Q_BIT, 0);
+ ao_enable_input_mask(N_PORT, N_MASK << N_SHIFT, 0);
+
+ /* Pins controlled by STM */
+ ao_enable_output_mask(EF_PORT, 0, EF_MASK << EF_SHIFT);
+ ao_enable_output(DMA_IN_PORT, DMA_IN_BIT, DMA_IN_PIN, 1);
+ ao_enable_output(DMA_OUT_PORT, DMA_OUT_BIT, DMA_OUT_PIN, 1);
+ ao_enable_output(INT_PORT, INT_BIT, INT_PIN, 1);
+ ao_enable_output(CLEAR_PORT, CLEAR_BIT, CLEAR_PIN, 1);
+ ao_enable_output(WAIT_PORT, WAIT_BIT, WAIT_PIN, 1);
+
+ /* Force configuration to STM so that MUX_1802 will do something */
+ MUX_CONTROL = _MUX_STM;
+ MUX_1802();
+}
diff --git a/src/cortexelf-v1/ao_1802.h b/src/cortexelf-v1/ao_1802.h
new file mode 100644
index 00000000..5ea89fee
--- /dev/null
+++ b/src/cortexelf-v1/ao_1802.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_1802_H_
+#define _AO_1802_H_
+
+/* Decoded address driven by TPA/TPB signals */
+extern uint16_t ADDRESS;
+
+/* Decoded data, driven by TPB signal */
+extern uint8_t DATA;
+
+uint8_t
+MRD(void);
+
+void
+MRD_set(uint8_t value);
+
+uint8_t
+MWR(void);
+
+void
+MWR_set(uint8_t value);
+
+uint8_t
+TPA(void);
+
+void
+TPA_set(uint8_t tpa);
+
+uint8_t
+TPB(void);
+
+void
+TPB_set(uint8_t tpb);
+
+uint8_t
+MA(void);
+
+void
+MA_set(uint8_t ma);
+
+/* Tri-state data bus */
+
+uint8_t
+BUS(void);
+
+void
+BUS_set(uint8_t bus);
+
+void
+BUS_stm(void);
+
+void
+BUS_1802(void);
+
+/* Pins controlled by 1802 */
+uint8_t
+SC(void);
+
+uint8_t
+Q(void);
+
+uint8_t
+N(void);
+
+/* Pins controlled by STM */
+uint8_t
+EF(void);
+
+void
+EF_set(uint8_t ef);
+
+uint8_t
+DMA_IN(void);
+
+void
+DMA_IN_set(uint8_t dma_in);
+
+uint8_t
+DMA_OUT(void);
+
+void
+DMA_OUT_set(uint8_t dma_out);
+
+uint8_t
+INT(void);
+
+void
+INT_set(uint8_t dma_out);
+
+uint8_t
+CLEAR(void);
+
+void
+CLEAR_set(uint8_t dma_out);
+
+uint8_t
+WAIT(void);
+
+void
+WAIT_set(uint8_t dma_out);
+
+#define SC_FETCH 0
+#define SC_EXECUTE 1
+#define SC_DMA 2
+#define SC_INTERRUPT 3
+
+void
+MUX_1802(void);
+
+void
+MUX_stm(void);
+
+void
+ao_1802_init(void);
+
+#endif /* _AO_1802_H_ */
diff --git a/src/cortexelf-v1/ao_cortexelf.c b/src/cortexelf-v1/ao_cortexelf.c
new file mode 100644
index 00000000..61a9d219
--- /dev/null
+++ b/src/cortexelf-v1/ao_cortexelf.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include <ao_profile.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_ps2.h>
+#include <ao_vga.h>
+#include <ao_console.h>
+#include <ao_sdcard.h>
+#include <ao_fat.h>
+#include <ao_lisp.h>
+#include <ao_button.h>
+#include <ao_event.h>
+#include <ao_as1107.h>
+#include <ao_hex.h>
+#include <ao_1802.h>
+
+struct ao_task ball_task;
+
+#define BALL_WIDTH 5
+#define BALL_HEIGHT 5
+
+static int ball_x;
+static int ball_y;
+static int ball_dx, ball_dy;
+
+uint8_t ball_enable;
+
+void
+ao_ball(void)
+{
+ ball_dx = 1;
+ ball_dy = 1;
+ ball_x = 0;
+ ball_y = 0;
+ for (;;) {
+ while (!ball_enable)
+ ao_sleep(&ball_enable);
+ for (;;) {
+ ao_line(&ao_vga_bitmap,
+ -100, -100, ball_x*2, ball_y*2,
+ 1, AO_XOR);
+ ao_text(&ao_vga_bitmap,
+ ball_x, ball_y - 10,
+ "Hello, Bdale!",
+ 1, AO_XOR);
+ ao_rect(&ao_vga_bitmap,
+ ball_x, ball_y,
+ BALL_WIDTH,
+ BALL_HEIGHT,
+ 1,
+ AO_XOR);
+ ao_delay(AO_MS_TO_TICKS(10));
+ ao_rect(&ao_vga_bitmap,
+ ball_x, ball_y,
+ BALL_WIDTH,
+ BALL_HEIGHT,
+ 1,
+ AO_XOR);
+ ao_text(&ao_vga_bitmap,
+ ball_x, ball_y - 10,
+ "Hello, Bdale!",
+ 1, AO_XOR);
+ ao_line(&ao_vga_bitmap,
+ -100, -100, ball_x*2, ball_y*2,
+ 1, AO_XOR);
+ if (!ball_enable)
+ break;
+ ball_x += ball_dx;
+ ball_y += ball_dy;
+ if (ball_x + BALL_WIDTH > AO_VGA_WIDTH) {
+ ball_x = AO_VGA_WIDTH - BALL_WIDTH;
+ ball_dx = -ball_dx;
+ }
+ if (ball_x < 0) {
+ ball_x = -ball_x;
+ ball_dx = -ball_dx;
+ }
+ if (ball_y + BALL_HEIGHT > AO_VGA_HEIGHT) {
+ ball_y = AO_VGA_HEIGHT - BALL_HEIGHT;
+ ball_dy = -ball_dy;
+ }
+ if (ball_y < 0) {
+ ball_y = -ball_y;
+ ball_dy = -ball_dy;
+ }
+ }
+ }
+}
+
+static void
+ao_fb_init(void)
+{
+ ao_rect(&ao_vga_bitmap,
+ 0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
+ 1, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ 10, 10, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ AO_VGA_WIDTH - 20, 10, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ 10, AO_VGA_HEIGHT - 20, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ AO_VGA_WIDTH - 20, AO_VGA_HEIGHT - 20, 10, 10,
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 20, 100,
+ "Hello, Bdale!",
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 1, ao_font.ascent,
+ "UL",
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 1, AO_VGA_HEIGHT - ao_font.descent,
+ "BL",
+ 0, AO_COPY);
+}
+
+static void
+ao_video_toggle(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_lex_i)
+ ao_fb_init();
+ ao_vga_enable(ao_cmd_lex_i);
+}
+
+static void
+ao_ball_toggle(void)
+{
+ ao_cmd_decimal();
+ ball_enable = ao_cmd_lex_i;
+ ao_wakeup(&ball_enable);
+}
+
+static void
+ao_ps2_read_keys(void)
+{
+ char c;
+
+ for (;;) {
+ c = ao_ps2_getchar();
+ printf("%02x %c\n", c, ' ' <= c && c < 0x7f ? c : '.');
+ flush();
+ if (c == ' ')
+ break;
+ }
+}
+
+static void
+ao_console_send(void)
+{
+ char c;
+
+ while ((c = getchar()) != '~') {
+ ao_console_putchar(c);
+ flush();
+ }
+}
+
+static void lisp_cmd() {
+ ao_lisp_read_eval_print();
+}
+
+static void
+ao_serial_blather(void)
+{
+ char c;
+
+ while ((c = getchar()) != '~') {
+ ao_serial1_putchar(c);
+ ao_serial2_putchar(c);
+ }
+}
+
+static void
+led_cmd(void)
+{
+ uint8_t start;
+ uint8_t value;
+ ao_cmd_decimal();
+
+ start = ao_cmd_lex_i;
+ ao_cmd_hex();
+ value = ao_cmd_lex_i;
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ ao_as1107_write_8(start, value);
+}
+
+__code struct ao_cmds ao_demo_cmds[] = {
+ { ao_video_toggle, "V\0Toggle video" },
+ { ao_ball_toggle, "B\0Toggle ball" },
+ { ao_ps2_read_keys, "K\0Read keys from keyboard" },
+ { ao_console_send, "C\0Send data to console, end with ~" },
+ { ao_serial_blather, "S\0Blather on serial ports briefly" },
+ { lisp_cmd, "l\0Run lisp interpreter" },
+ { led_cmd, "L start value\0Show value (byte) at digit start" },
+ { 0, NULL }
+};
+
+static struct ao_task event_task;
+
+static void
+ao_event_loop(void)
+{
+ for (;;) {
+ struct ao_event ev;
+
+ ao_event_get(&ev);
+ printf("type %d uint %d tick %d value %d\n",
+ ev.type, ev.unit, ev.tick, ev.value);
+ flush();
+ }
+}
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_serial_init();
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_sdcard_init();
+ ao_fat_init();
+
+ ao_ps2_init();
+ ao_vga_init();
+ ao_console_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+
+ ao_button_init();
+
+ ao_as1107_init();
+ ao_matrix_init();
+ ao_1802_init();
+
+ ao_hex_init();
+
+ ao_config_init();
+
+ ao_add_task(&ball_task, ao_ball, "ball");
+ ao_add_task(&event_task, ao_event_loop, "events");
+ ao_cmd_register(&ao_demo_cmds[0]);
+
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/cortexelf-v1/ao_flip_bits.5c b/src/cortexelf-v1/ao_flip_bits.5c
new file mode 100644
index 00000000..cd5507cc
--- /dev/null
+++ b/src/cortexelf-v1/ao_flip_bits.5c
@@ -0,0 +1,24 @@
+#!/usr/bin/nickle
+
+int flip_bits(int a, int n)
+{
+ int result = 0;
+ for (int pos = 0; pos < n; pos++)
+ if ((a & (1 << pos)) != 0)
+ result |= (1 << (n - 1 - pos));
+ return result;
+}
+
+void print_flip_bits(string name, int n) {
+ printf ("static const uint8_t %s_%d[%d] = {\n", name, n, 1 << n);
+
+ for (int i = 0; i < 1 << n; i++) {
+ printf (" 0x%02x,", flip_bits(i, n));
+ if ((i & 0xf) == 0xf)
+ printf("\n");
+ }
+ printf("};\n");
+}
+
+print_flip_bits("ao_flip_bits", 8);
+print_flip_bits("ao_flip_bits", 2);
diff --git a/src/cortexelf-v1/ao_hex.c b/src/cortexelf-v1/ao_hex.c
new file mode 100644
index 00000000..1507407b
--- /dev/null
+++ b/src/cortexelf-v1/ao_hex.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include "ao_hex.h"
+#include "ao_as1107.h"
+#include "ao_1802.h"
+
+static struct ao_task ao_hex_task;
+
+static void
+ao_hex(void)
+{
+ for (;;) {
+ ao_as1107_write_16(0, ADDRESS);
+ ao_as1107_write_8(6, DATA);
+ ao_sleep(&ADDRESS);
+ }
+}
+
+void
+ao_hex_init(void)
+{
+ ao_add_task(&ao_hex_task, ao_hex, "hex");
+}
diff --git a/src/cortexelf-v1/ao_hex.h b/src/cortexelf-v1/ao_hex.h
new file mode 100644
index 00000000..674c1eee
--- /dev/null
+++ b/src/cortexelf-v1/ao_hex.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_HEX_H_
+#define _AO_HEX_H_
+
+void
+ao_hex_init(void);
+
+#endif /* _AO_HEX_H_ */
diff --git a/src/cortexelf-v1/ao_lisp_os.h b/src/cortexelf-v1/ao_lisp_os.h
new file mode 100644
index 00000000..d0c1f7b7
--- /dev/null
+++ b/src/cortexelf-v1/ao_lisp_os.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LISP_OS_H_
+#define _AO_LISP_OS_H_
+
+#include "ao.h"
+
+#define AO_LISP_POOL_TOTAL 16384
+#define AO_LISP_SAVE 1
+
+static inline int
+ao_lisp_getc() {
+ static uint8_t at_eol;
+ int c;
+
+ if (at_eol) {
+ ao_cmd_readline();
+ at_eol = 0;
+ }
+ c = ao_cmd_lex();
+ if (c == '\n')
+ at_eol = 1;
+ return c;
+}
+
+static inline void
+ao_lisp_os_flush(void)
+{
+ flush();
+}
+
+static inline void
+ao_lisp_abort(void)
+{
+ ao_panic(1);
+}
+
+static inline void
+ao_lisp_os_led(int led)
+{
+ (void) led;
+}
+
+static inline void
+ao_lisp_os_delay(int delay)
+{
+ ao_delay(AO_MS_TO_TICKS(delay));
+}
+
+#endif
diff --git a/src/cortexelf-v1/ao_lisp_os_save.c b/src/cortexelf-v1/ao_lisp_os_save.c
new file mode 100644
index 00000000..7c853990
--- /dev/null
+++ b/src/cortexelf-v1/ao_lisp_os_save.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_lisp.h>
+#include <ao_flash.h>
+
+extern uint8_t __flash__[];
+
+/* saved variables to rebuild the heap
+
+ ao_lisp_atoms
+ ao_lisp_frame_global
+ */
+
+int
+ao_lisp_os_save(void)
+{
+ int i;
+
+ for (i = 0; i < AO_LISP_POOL_TOTAL; i += 256) {
+ uint32_t *dst = (uint32_t *) (void *) &__flash__[i];
+ uint32_t *src = (uint32_t *) (void *) &ao_lisp_pool[i];
+
+ ao_flash_page(dst, src);
+ }
+ return 1;
+}
+
+int
+ao_lisp_os_restore_save(struct ao_lisp_os_save *save, int offset)
+{
+ memcpy(save, &__flash__[offset], sizeof (struct ao_lisp_os_save));
+ return 1;
+}
+
+int
+ao_lisp_os_restore(void)
+{
+ memcpy(ao_lisp_pool, __flash__, AO_LISP_POOL_TOTAL);
+ return 1;
+}
diff --git a/src/cortexelf-v1/ao_pins.h b/src/cortexelf-v1/ao_pins.h
new file mode 100644
index 00000000..258ffe31
--- /dev/null
+++ b/src/cortexelf-v1/ao_pins.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 24MHz */
+#define AO_PLLDIV 4
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_4)
+
+/* HCLK = 24MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at HCLK/1 */
+#define AO_APB1_PRESCALER 1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_1
+
+/* Run APB2 at HCLK/1 */
+#define AO_APB2_PRESCALER 1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+
+/* Allow for non-maskable interrupts at priority 0 */
+#define AO_NONMASK_INTERRUPT 1
+
+/* PS/2 keyboard connection */
+#define AO_PS2_CLOCK_PORT (&stm_gpiod)
+#define AO_PS2_CLOCK_BIT 9
+#define AO_PS2_DATA_PORT (&stm_gpiod)
+#define AO_PS2_DATA_BIT 8
+
+#define HAS_SERIAL_1 1
+#define USE_SERIAL_1_STDIN 1
+#define SERIAL_1_PB6_PB7 1
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 1
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 1
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define HAS_EEPROM 0
+#define USE_INTERNAL_FLASH 0
+#define USE_EEPROM_CONFIG 0
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define HAS_BATTERY_REPORT 0
+#define HAS_RADIO 0
+#define HAS_TELEMETRY 0
+#define HAS_APRS 0
+#define HAS_COMPANION 0
+
+#define HAS_SPI_1 0
+#define SPI_1_PA5_PA6_PA7 0
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 0
+#define SPI_2_PD1_PD3_PD4 1 /* LED displays, microSD */
+#define SPI_2_OSPEEDR STM_OSPEEDR_40MHz
+
+#define SPI_2_PORT (&stm_gpiod)
+//#define SPI_2_SCK_PIN 1
+//#define SPI_2_MISO_PIN 3
+//#define SPI_2_MOSI_PIN 4
+
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 0
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 0
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 0
+
+#define LOW_LEVEL_DEBUG 0
+
+#define HAS_GPS 0
+#define HAS_FLIGHT 0
+#define HAS_ADC 0
+#define HAS_ADC_TEMP 0
+#define HAS_LOG 0
+
+#define NUM_CMDS 16
+
+/* SD card */
+#define AO_SDCARD_SPI_BUS AO_SPI_2_PD1_PD3_PD4
+#define AO_SDCARD_SPI_CS_PORT (&stm_gpiod)
+#define AO_SDCARD_SPI_CS_PIN 2
+#define AO_SDCARD_SPI_PORT (&stm_gpiod)
+#define AO_SDCARD_SPI_SCK_PIN 1
+#define AO_SDCARD_SPI_MISO_PIN 3
+#define AO_SDCARD_SPI_MOSI_PIN 4
+
+/* VGA */
+#define STM_DMA1_3_STOLEN 1
+/* Buttons */
+
+#define AO_EVENT 1
+
+#define AO_BUTTON_COUNT 4
+#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_DOWN
+#define AO_BUTTON_INVERTED 0
+
+/* INPUT */
+#define AO_BUTTON_0_PORT (&stm_gpioc)
+#define AO_BUTTON_0 8
+
+/* MP */
+#define AO_BUTTON_1_PORT (&stm_gpioc)
+#define AO_BUTTON_1 9
+
+/* RUN */
+#define AO_BUTTON_2_PORT (&stm_gpioc)
+#define AO_BUTTON_2 10
+
+/* LOAD */
+#define AO_BUTTON_3_PORT (&stm_gpioc)
+#define AO_BUTTON_3 11
+
+/* AS1107 */
+#define AO_AS1107_NUM_DIGITS 8
+
+/* Set the hex digits up for decode, leave the extra leds alone */
+
+#define AO_AS1107_DECODE ((1 << 7) | \
+ (1 << 6) | \
+ (1 << 4) | \
+ (1 << 3) | \
+ (1 << 1) | \
+ (1 << 0))
+
+#define AO_AS1107_SPI_INDEX AO_SPI_2_PD1_PD3_PD4
+#define AO_AS1107_SPI_SPEED AO_SPI_SPEED_8MHz
+#define AO_AS1107_CS_PORT (&stm_gpiod)
+#define AO_AS1107_CS_PIN 0
+
+/* Hex keypad */
+
+#define AO_MATRIX_ROWS 4
+#define AO_MATRIX_COLS 4
+
+#define AO_MATRIX_KEYCODES { \
+ { 0x0, 0x1, 0x2, 0x3 }, \
+ { 0x4, 0x5, 0x6, 0x7 }, \
+ { 0x8, 0x9, 0xa, 0xb }, \
+ { 0xc, 0xd, 0xe, 0xf } \
+ }
+
+#include <ao_matrix.h>
+
+#define AO_TIMER_HOOK ao_matrix_poll()
+
+#define AO_MATRIX_ROW_0_PORT (&stm_gpioc)
+#define AO_MATRIX_ROW_0_PIN 4
+
+#define AO_MATRIX_ROW_1_PORT (&stm_gpioc)
+#define AO_MATRIX_ROW_1_PIN 1
+
+#define AO_MATRIX_ROW_2_PORT (&stm_gpioc)
+#define AO_MATRIX_ROW_2_PIN 7
+
+#define AO_MATRIX_ROW_3_PORT (&stm_gpioc)
+#define AO_MATRIX_ROW_3_PIN 0
+
+#define AO_MATRIX_COL_0_PORT (&stm_gpioc)
+#define AO_MATRIX_COL_0_PIN 2
+
+#define AO_MATRIX_COL_1_PORT (&stm_gpioc)
+#define AO_MATRIX_COL_1_PIN 3
+
+#define AO_MATRIX_COL_2_PORT (&stm_gpioc)
+#define AO_MATRIX_COL_2_PIN 5
+
+#define AO_MATRIX_COL_3_PORT (&stm_gpioc)
+#define AO_MATRIX_COL_3_PIN 6
+
+/* 1802 connections */
+#define MRD_PORT (&stm_gpiob)
+#define MRD_BIT 15
+
+#define MWR_PORT (&stm_gpioa)
+#define MWR_BIT 3
+
+#define TPB_PORT (&stm_gpioa)
+#define TPB_BIT 7
+
+#define TPA_PORT (&stm_gpioa)
+#define TPA_BIT 6
+
+#define MA_PORT (&stm_gpioe)
+#define MA_SHIFT 0
+#define MA_MASK 0xff
+
+#define BUS_PORT (&stm_gpioe)
+#define BUS_SHIFT 8
+#define BUS_MASK 0xff
+
+#define SC_PORT (&stm_gpiob)
+#define SC_SHIFT 13
+#define SC_MASK 3
+
+#define Q_PORT (&stm_gpiob)
+#define Q_BIT 12
+
+#define N_PORT (&stm_gpiod)
+#define N_SHIFT 13
+#define N_MASK 7
+
+#define EF_PORT (&stm_gpiob)
+#define EF_SHIFT 8
+#define EF_MASK 0xf
+
+#define DMA_IN_PORT (&stm_gpioa)
+#define DMA_IN_BIT 0
+
+#define DMA_OUT_PORT (&stm_gpioa)
+#define DMA_OUT_BIT 9
+
+#define INT_PORT (&stm_gpioa)
+#define INT_BIT 2
+
+#define CLEAR_PORT (&stm_gpioa)
+#define CLEAR_BIT 10
+
+#define WAIT_PORT (&stm_gpioa)
+#define WAIT_BIT 4
+
+#define MUX_PORT (&stm_gpiob)
+#define MUX_BIT 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/cortexelf-v1/cortexelf.ld b/src/cortexelf-v1/cortexelf.ld
new file mode 100644
index 00000000..6ad2a679
--- /dev/null
+++ b/src/cortexelf-v1/cortexelf.ld
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08001000, LENGTH = 492K
+ flash (r) : ORIGIN = 0x0807c000, LENGTH = 16K
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 81408
+ stack (!w) : ORIGIN = 0x20013e00, LENGTH = 512
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .text ORIGIN(rom) : {
+ __text_start__ = .;
+ *(.interrupt) /* Interrupt vectors */
+
+ . = ORIGIN(rom) + 0x100;
+
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+ *(.text*) /* Executable code */
+ *(.rodata*) /* Constants */
+
+ } > rom
+
+ .ARM.exidx : {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } > rom
+ __text_end__ = .;
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ . = ALIGN(4);
+ __boot_end__ = .;
+ } >ram
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ __data_start__ = .;
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+
+ __flash__ = ORIGIN(flash);
+}
+
+ENTRY(start);
+
+
diff --git a/src/cortexelf-v1/flash-loader/Makefile b/src/cortexelf-v1/flash-loader/Makefile
new file mode 100644
index 00000000..19cf84e4
--- /dev/null
+++ b/src/cortexelf-v1/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=cortexelf-v1
+include $(TOPDIR)/stm/Makefile-flash.defs
diff --git a/src/cortexelf-v1/flash-loader/ao_pins.h b/src/cortexelf-v1/flash-loader/ao_pins.h
new file mode 100644
index 00000000..5d63dc2c
--- /dev/null
+++ b/src/cortexelf-v1/flash-loader/ao_pins.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 8MHz */
+#define AO_HSE 8000000
+
+#include <ao_flash_stm_pins.h>
+
+/* MP switch, gpioc 9 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioc
+#define AO_BOOT_APPLICATION_PIN 9
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/draw/5x7.bdf b/src/draw/5x7.bdf
new file mode 100644
index 00000000..b511f289
--- /dev/null
+++ b/src/draw/5x7.bdf
@@ -0,0 +1,3190 @@
+STARTFONT 2.1
+COMMENT Copyright 1991 Massachusetts Institute of Technology
+COMMENT
+COMMENT Permission to use, copy, modify, and distribute this software
+COMMENT and its documentation for any purpose and without fee is
+COMMENT hereby granted, provided that the above copyright notice
+COMMENT appear in all copies and that both that copyright notice and
+COMMENT this permission notice appear in supporting documentation,
+COMMENT and that the name of M.I.T. not be used in advertising or
+COMMENT publicity pertaining to distribution of the software without
+COMMENT specific, written prior permission. M.I.T. makes no
+COMMENT representations about the suitability of this software for
+COMMENT any purpose. It is provided "as is" without express or
+COMMENT implied warranty.
+COMMENT
+COMMENT M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+COMMENT INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+COMMENT FITNESS, IN NO EVENT SHALL M.I.T. BE LIABLE FOR ANY SPECIAL,
+COMMENT INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+COMMENT RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+COMMENT ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+COMMENT ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+COMMENT OF THIS SOFTWARE.
+COMMENT
+COMMENT Author: Stephen Gildea, MIT X Consortium, June 1991
+COMMENT
+FONT -Misc-Fixed-Medium-R-Normal--7-70-75-75-C-50-ISO8859-1
+SIZE 7 75 75
+FONTBOUNDINGBOX 5 7 0 -1
+STARTPROPERTIES 21
+FONTNAME_REGISTRY ""
+FOUNDRY "Misc"
+FAMILY_NAME "Fixed"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 7
+POINT_SIZE 70
+RESOLUTION_X 75
+RESOLUTION_Y 75
+SPACING "C"
+AVERAGE_WIDTH 50
+CHARSET_REGISTRY "ISO8859"
+CHARSET_ENCODING "1"
+FONT_ASCENT 6
+FONT_DESCENT 1
+UNDERLINE_POSITION 0
+DESTINATION 1
+DEFAULT_CHAR 0
+COPYRIGHT "Copyright 1991 Massachusetts Institute of Technology"
+ENDPROPERTIES
+CHARS 224
+STARTCHAR C000
+ENCODING 0
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+f0
+f0
+f0
+f0
+f0
+00
+ENDCHAR
+STARTCHAR C001
+ENCODING 1
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+20
+70
+f8
+70
+20
+00
+ENDCHAR
+STARTCHAR C002
+ENCODING 2
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+a0
+50
+a0
+50
+a0
+00
+ENDCHAR
+STARTCHAR C003
+ENCODING 3
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+e0
+a0
+a0
+70
+20
+20
+ENDCHAR
+STARTCHAR C004
+ENCODING 4
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+c0
+80
+c0
+b0
+20
+30
+20
+ENDCHAR
+STARTCHAR C005
+ENCODING 5
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+c0
+80
+c0
+60
+50
+60
+50
+ENDCHAR
+STARTCHAR C006
+ENCODING 6
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+c0
+30
+20
+30
+20
+ENDCHAR
+STARTCHAR C007
+ENCODING 7
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+50
+20
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C010
+ENCODING 8
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+70
+20
+00
+70
+00
+00
+ENDCHAR
+STARTCHAR C011
+ENCODING 9
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+d0
+b0
+90
+20
+20
+30
+ENDCHAR
+STARTCHAR C012
+ENCODING 10
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+a0
+a0
+40
+70
+20
+20
+ENDCHAR
+STARTCHAR C013
+ENCODING 11
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+e0
+00
+00
+00
+ENDCHAR
+STARTCHAR C014
+ENCODING 12
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+e0
+20
+20
+20
+ENDCHAR
+STARTCHAR C015
+ENCODING 13
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+38
+20
+20
+20
+ENDCHAR
+STARTCHAR C016
+ENCODING 14
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+38
+00
+00
+00
+ENDCHAR
+STARTCHAR C017
+ENCODING 15
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+f8
+20
+20
+20
+ENDCHAR
+STARTCHAR C020
+ENCODING 16
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+f8
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C021
+ENCODING 17
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 6 7 0 -1
+BITMAP
+00
+00
+f8
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C022
+ENCODING 18
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+f8
+00
+00
+00
+ENDCHAR
+STARTCHAR C023
+ENCODING 19
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+f8
+00
+00
+ENDCHAR
+STARTCHAR C024
+ENCODING 20
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+f8
+00
+ENDCHAR
+STARTCHAR C025
+ENCODING 21
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+38
+20
+20
+20
+ENDCHAR
+STARTCHAR C026
+ENCODING 22
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+e0
+20
+20
+20
+ENDCHAR
+STARTCHAR C027
+ENCODING 23
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 6 7 0 -1
+BITMAP
+20
+20
+20
+f8
+00
+00
+00
+ENDCHAR
+STARTCHAR C030
+ENCODING 24
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+f8
+20
+20
+20
+ENDCHAR
+STARTCHAR C031
+ENCODING 25
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+20
+20
+20
+20
+ENDCHAR
+STARTCHAR C032
+ENCODING 26
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+10
+20
+40
+20
+10
+70
+00
+ENDCHAR
+STARTCHAR C033
+ENCODING 27
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+10
+20
+40
+70
+00
+ENDCHAR
+STARTCHAR C034
+ENCODING 28
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+50
+50
+50
+00
+ENDCHAR
+STARTCHAR C035
+ENCODING 29
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+10
+70
+20
+70
+40
+00
+ENDCHAR
+STARTCHAR C036
+ENCODING 30
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+30
+40
+e0
+40
+b0
+00
+ENDCHAR
+STARTCHAR C037
+ENCODING 31
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+20
+00
+00
+00
+ENDCHAR
+STARTCHAR C040
+ENCODING 32
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR !
+ENCODING 33
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+20
+20
+00
+20
+00
+ENDCHAR
+STARTCHAR "
+ENCODING 34
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+50
+50
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR #
+ENCODING 35
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+50
+f8
+50
+f8
+50
+00
+ENDCHAR
+STARTCHAR $
+ENCODING 36
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+70
+a0
+70
+28
+70
+00
+ENDCHAR
+STARTCHAR %
+ENCODING 37
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+90
+20
+40
+90
+10
+00
+ENDCHAR
+STARTCHAR &
+ENCODING 38
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+40
+a0
+40
+a0
+50
+00
+ENDCHAR
+STARTCHAR '
+ENCODING 39
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+40
+80
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR (
+ENCODING 40
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+40
+40
+40
+20
+00
+ENDCHAR
+STARTCHAR )
+ENCODING 41
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+20
+20
+20
+40
+00
+ENDCHAR
+STARTCHAR *
+ENCODING 42
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+a0
+40
+e0
+40
+a0
+00
+ENDCHAR
+STARTCHAR +
+ENCODING 43
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+20
+20
+f8
+20
+20
+00
+ENDCHAR
+STARTCHAR ,
+ENCODING 44
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+60
+40
+80
+ENDCHAR
+STARTCHAR -
+ENCODING 45
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+f0
+00
+00
+00
+ENDCHAR
+STARTCHAR .
+ENCODING 46
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+60
+60
+00
+ENDCHAR
+STARTCHAR /
+ENCODING 47
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+10
+20
+40
+80
+00
+00
+ENDCHAR
+STARTCHAR 0
+ENCODING 48
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+a0
+a0
+a0
+40
+00
+ENDCHAR
+STARTCHAR 1
+ENCODING 49
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+c0
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR 2
+ENCODING 50
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+10
+20
+40
+f0
+00
+ENDCHAR
+STARTCHAR 3
+ENCODING 51
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+10
+60
+10
+90
+60
+00
+ENDCHAR
+STARTCHAR 4
+ENCODING 52
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+60
+a0
+f0
+20
+20
+00
+ENDCHAR
+STARTCHAR 5
+ENCODING 53
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+10
+90
+60
+00
+ENDCHAR
+STARTCHAR 6
+ENCODING 54
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+80
+e0
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR 7
+ENCODING 55
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+10
+20
+20
+40
+40
+00
+ENDCHAR
+STARTCHAR 8
+ENCODING 56
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR 9
+ENCODING 57
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+70
+10
+60
+00
+ENDCHAR
+STARTCHAR :
+ENCODING 58
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+60
+60
+00
+60
+60
+00
+ENDCHAR
+STARTCHAR ;
+ENCODING 59
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+60
+60
+00
+60
+40
+80
+ENDCHAR
+STARTCHAR <
+ENCODING 60
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+20
+40
+80
+40
+20
+00
+ENDCHAR
+STARTCHAR =
+ENCODING 61
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+f0
+00
+f0
+00
+00
+ENDCHAR
+STARTCHAR >
+ENCODING 62
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+80
+40
+20
+40
+80
+00
+ENDCHAR
+STARTCHAR ?
+ENCODING 63
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+20
+40
+00
+40
+00
+ENDCHAR
+STARTCHAR @
+ENCODING 64
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+b0
+b0
+80
+60
+00
+ENDCHAR
+STARTCHAR A
+ENCODING 65
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+90
+e0
+90
+90
+e0
+00
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+80
+80
+90
+60
+00
+ENDCHAR
+STARTCHAR D
+ENCODING 68
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+90
+90
+90
+90
+e0
+00
+ENDCHAR
+STARTCHAR E
+ENCODING 69
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR F
+ENCODING 70
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+80
+00
+ENDCHAR
+STARTCHAR G
+ENCODING 71
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+80
+b0
+90
+70
+00
+ENDCHAR
+STARTCHAR H
+ENCODING 72
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+f0
+90
+90
+90
+00
+ENDCHAR
+STARTCHAR I
+ENCODING 73
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR J
+ENCODING 74
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+10
+10
+10
+10
+90
+60
+00
+ENDCHAR
+STARTCHAR K
+ENCODING 75
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+a0
+c0
+c0
+a0
+90
+00
+ENDCHAR
+STARTCHAR L
+ENCODING 76
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+80
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR M
+ENCODING 77
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+f0
+f0
+90
+90
+90
+00
+ENDCHAR
+STARTCHAR N
+ENCODING 78
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+d0
+d0
+b0
+b0
+90
+00
+ENDCHAR
+STARTCHAR O
+ENCODING 79
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR P
+ENCODING 80
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+90
+90
+e0
+80
+80
+00
+ENDCHAR
+STARTCHAR Q
+ENCODING 81
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+d0
+60
+10
+ENDCHAR
+STARTCHAR R
+ENCODING 82
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+90
+90
+e0
+a0
+90
+00
+ENDCHAR
+STARTCHAR S
+ENCODING 83
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+40
+20
+90
+60
+00
+ENDCHAR
+STARTCHAR T
+ENCODING 84
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+40
+00
+ENDCHAR
+STARTCHAR U
+ENCODING 85
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR V
+ENCODING 86
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+60
+60
+00
+ENDCHAR
+STARTCHAR W
+ENCODING 87
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+f0
+f0
+90
+00
+ENDCHAR
+STARTCHAR X
+ENCODING 88
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+60
+60
+90
+90
+00
+ENDCHAR
+STARTCHAR Y
+ENCODING 89
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+a0
+a0
+40
+40
+40
+00
+ENDCHAR
+STARTCHAR Z
+ENCODING 90
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+10
+20
+40
+80
+f0
+00
+ENDCHAR
+STARTCHAR [
+ENCODING 91
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+80
+80
+80
+80
+e0
+00
+ENDCHAR
+STARTCHAR \
+ENCODING 92
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+80
+40
+20
+10
+00
+00
+ENDCHAR
+STARTCHAR ]
+ENCODING 93
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+20
+20
+20
+20
+e0
+00
+ENDCHAR
+STARTCHAR ^
+ENCODING 94
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR _
+ENCODING 95
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+f0
+00
+ENDCHAR
+STARTCHAR `
+ENCODING 96
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+c0
+40
+20
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR a
+ENCODING 97
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR b
+ENCODING 98
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+e0
+90
+90
+e0
+00
+ENDCHAR
+STARTCHAR c
+ENCODING 99
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+60
+80
+80
+60
+00
+ENDCHAR
+STARTCHAR d
+ENCODING 100
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+10
+10
+70
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR e
+ENCODING 101
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+60
+b0
+c0
+60
+00
+ENDCHAR
+STARTCHAR f
+ENCODING 102
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+50
+40
+e0
+40
+40
+00
+ENDCHAR
+STARTCHAR g
+ENCODING 103
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+90
+60
+80
+70
+ENDCHAR
+STARTCHAR h
+ENCODING 104
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+e0
+90
+90
+90
+00
+ENDCHAR
+STARTCHAR i
+ENCODING 105
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+00
+c0
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR j
+ENCODING 106
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+00
+20
+20
+20
+a0
+40
+ENDCHAR
+STARTCHAR k
+ENCODING 107
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+a0
+c0
+a0
+90
+00
+ENDCHAR
+STARTCHAR l
+ENCODING 108
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+c0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR m
+ENCODING 109
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+a0
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR n
+ENCODING 110
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+e0
+90
+90
+90
+00
+ENDCHAR
+STARTCHAR o
+ENCODING 111
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR p
+ENCODING 112
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+e0
+90
+90
+e0
+80
+ENDCHAR
+STARTCHAR q
+ENCODING 113
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+90
+90
+70
+10
+ENDCHAR
+STARTCHAR r
+ENCODING 114
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+e0
+90
+80
+80
+00
+ENDCHAR
+STARTCHAR s
+ENCODING 115
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 6 7 0 -1
+BITMAP
+00
+00
+70
+c0
+30
+e0
+00
+ENDCHAR
+STARTCHAR t
+ENCODING 116
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+40
+e0
+40
+40
+30
+00
+ENDCHAR
+STARTCHAR u
+ENCODING 117
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR v
+ENCODING 118
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+a0
+a0
+a0
+40
+00
+ENDCHAR
+STARTCHAR w
+ENCODING 119
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+90
+f0
+f0
+00
+ENDCHAR
+STARTCHAR x
+ENCODING 120
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+60
+60
+90
+00
+ENDCHAR
+STARTCHAR y
+ENCODING 121
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+90
+50
+20
+40
+ENDCHAR
+STARTCHAR z
+ENCODING 122
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+f0
+20
+40
+f0
+00
+ENDCHAR
+STARTCHAR {
+ENCODING 123
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+c0
+40
+40
+20
+00
+ENDCHAR
+STARTCHAR |
+ENCODING 124
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+40
+40
+40
+40
+40
+00
+ENDCHAR
+STARTCHAR }
+ENCODING 125
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 6 7 0 -1
+BITMAP
+80
+40
+60
+40
+40
+80
+00
+ENDCHAR
+STARTCHAR ~
+ENCODING 126
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+a0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR Blank
+ENCODING 127
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C160
+ENCODING 160
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C161
+ENCODING 161
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+00
+20
+20
+20
+20
+00
+ENDCHAR
+STARTCHAR C162
+ENCODING 162
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+20
+70
+a0
+a0
+70
+20
+ENDCHAR
+STARTCHAR C163
+ENCODING 163
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+30
+40
+e0
+40
+b0
+00
+ENDCHAR
+STARTCHAR C164
+ENCODING 164
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+88
+70
+50
+70
+88
+00
+ENDCHAR
+STARTCHAR C165
+ENCODING 165
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+a0
+40
+e0
+40
+40
+00
+ENDCHAR
+STARTCHAR C166
+ENCODING 166
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+20
+20
+00
+20
+20
+00
+ENDCHAR
+STARTCHAR C167
+ENCODING 167
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+30
+40
+60
+50
+30
+10
+60
+ENDCHAR
+STARTCHAR C168
+ENCODING 168
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C169
+ENCODING 169
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+70
+88
+a8
+c8
+a8
+88
+70
+ENDCHAR
+STARTCHAR C170
+ENCODING 170
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+a0
+60
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C171
+ENCODING 171
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+48
+90
+48
+00
+00
+ENDCHAR
+STARTCHAR C172
+ENCODING 172
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+f0
+10
+00
+00
+00
+ENDCHAR
+STARTCHAR C173
+ENCODING 173
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+f0
+00
+00
+00
+ENDCHAR
+STARTCHAR C174
+ENCODING 174
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+70
+88
+e8
+c8
+c8
+88
+70
+ENDCHAR
+STARTCHAR C175
+ENCODING 175
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C176
+ENCODING 176
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+50
+20
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C177
+ENCODING 177
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+20
+f8
+20
+20
+f8
+00
+ENDCHAR
+STARTCHAR C178
+ENCODING 178
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+20
+40
+60
+00
+00
+00
+ENDCHAR
+STARTCHAR C179
+ENCODING 179
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+60
+20
+60
+00
+00
+00
+ENDCHAR
+STARTCHAR C180
+ENCODING 180
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C181
+ENCODING 181
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+90
+90
+e0
+80
+ENDCHAR
+STARTCHAR C182
+ENCODING 182
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+70
+d0
+d0
+50
+50
+50
+00
+ENDCHAR
+STARTCHAR C183
+ENCODING 183
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+60
+60
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C184
+ENCODING 184
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+00
+00
+00
+20
+40
+ENDCHAR
+STARTCHAR C185
+ENCODING 185
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+60
+20
+70
+00
+00
+00
+ENDCHAR
+STARTCHAR C186
+ENCODING 186
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+40
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C187
+ENCODING 187
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+48
+90
+00
+00
+ENDCHAR
+STARTCHAR C188
+ENCODING 188
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+80
+90
+30
+70
+10
+ENDCHAR
+STARTCHAR C189
+ENCODING 189
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+80
+80
+b0
+10
+20
+30
+ENDCHAR
+STARTCHAR C190
+ENCODING 190
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+c0
+c0
+40
+d0
+30
+70
+10
+ENDCHAR
+STARTCHAR C191
+ENCODING 191
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+00
+40
+80
+a0
+40
+00
+ENDCHAR
+STARTCHAR Agrave
+ENCODING 192
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C193
+ENCODING 193
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C194
+ENCODING 194
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C195
+ENCODING 195
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C196
+ENCODING 196
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C197
+ENCODING 197
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+f0
+90
+90
+00
+ENDCHAR
+STARTCHAR C198
+ENCODING 198
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+70
+a0
+b0
+e0
+a0
+b0
+00
+ENDCHAR
+STARTCHAR C199
+ENCODING 199
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+80
+80
+90
+60
+40
+ENDCHAR
+STARTCHAR Egrave
+ENCODING 200
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR C201
+ENCODING 201
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR C202
+ENCODING 202
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR C203
+ENCODING 203
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+f0
+80
+e0
+80
+80
+f0
+00
+ENDCHAR
+STARTCHAR C204
+ENCODING 204
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C205
+ENCODING 205
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C206
+ENCODING 206
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C207
+ENCODING 207
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+40
+40
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C208
+ENCODING 208
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+e0
+50
+d0
+50
+50
+e0
+00
+ENDCHAR
+STARTCHAR C209
+ENCODING 209
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+b0
+90
+d0
+b0
+b0
+90
+00
+ENDCHAR
+STARTCHAR Ograve
+ENCODING 210
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C211
+ENCODING 211
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C212
+ENCODING 212
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C213
+ENCODING 213
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C214
+ENCODING 214
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C215
+ENCODING 215
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+90
+60
+60
+90
+00
+ENDCHAR
+STARTCHAR C216
+ENCODING 216
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+70
+b0
+b0
+d0
+d0
+e0
+00
+ENDCHAR
+STARTCHAR Ugrave
+ENCODING 217
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C218
+ENCODING 218
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C219
+ENCODING 219
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C220
+ENCODING 220
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+90
+90
+90
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C221
+ENCODING 221
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+a0
+a0
+40
+40
+40
+00
+ENDCHAR
+STARTCHAR C222
+ENCODING 222
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+e0
+90
+e0
+80
+80
+00
+ENDCHAR
+STARTCHAR C223
+ENCODING 223
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+90
+e0
+90
+d0
+a0
+80
+ENDCHAR
+STARTCHAR a-grave
+ENCODING 224
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C225
+ENCODING 225
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C226
+ENCODING 226
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+50
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C227
+ENCODING 227
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+a0
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C228
+ENCODING 228
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+00
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C229
+ENCODING 229
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+60
+70
+90
+b0
+50
+00
+ENDCHAR
+STARTCHAR C230
+ENCODING 230
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+b0
+a0
+70
+00
+ENDCHAR
+STARTCHAR C231
+ENCODING 231
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+60
+80
+80
+60
+40
+ENDCHAR
+STARTCHAR e-grave
+ENCODING 232
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+60
+b0
+c0
+60
+00
+ENDCHAR
+STARTCHAR C233
+ENCODING 233
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+60
+b0
+c0
+60
+00
+ENDCHAR
+STARTCHAR C234
+ENCODING 234
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+60
+b0
+c0
+60
+00
+ENDCHAR
+STARTCHAR C235
+ENCODING 235
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+00
+60
+b0
+c0
+60
+00
+ENDCHAR
+STARTCHAR C236
+ENCODING 236
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+80
+40
+c0
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C237
+ENCODING 237
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+80
+c0
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C238
+ENCODING 238
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+a0
+c0
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C239
+ENCODING 239
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+00
+c0
+40
+40
+e0
+00
+ENDCHAR
+STARTCHAR C240
+ENCODING 240
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+30
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C241
+ENCODING 241
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+a0
+e0
+90
+90
+90
+00
+ENDCHAR
+STARTCHAR C242
+ENCODING 242
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C243
+ENCODING 243
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C244
+ENCODING 244
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+00
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C245
+ENCODING 245
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+a0
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C246
+ENCODING 246
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+a0
+00
+60
+90
+90
+60
+00
+ENDCHAR
+STARTCHAR C247
+ENCODING 247
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+60
+00
+f0
+00
+60
+00
+ENDCHAR
+STARTCHAR C248
+ENCODING 248
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+00
+70
+b0
+d0
+e0
+00
+ENDCHAR
+STARTCHAR C249
+ENCODING 249
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+40
+20
+90
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR C250
+ENCODING 250
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+90
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR C251
+ENCODING 251
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+60
+00
+90
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR C252
+ENCODING 252
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+00
+90
+90
+90
+70
+00
+ENDCHAR
+STARTCHAR C253
+ENCODING 253
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+20
+40
+90
+90
+50
+20
+40
+ENDCHAR
+STARTCHAR C254
+ENCODING 254
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+00
+80
+e0
+90
+90
+e0
+80
+ENDCHAR
+STARTCHAR C255
+ENCODING 255
+SWIDTH 686 0
+DWIDTH 5 0
+BBX 5 7 0 -1
+BITMAP
+50
+00
+90
+90
+50
+20
+40
+ENDCHAR
+ENDFONT
diff --git a/src/draw/Makefile b/src/draw/Makefile
new file mode 100644
index 00000000..0a542a1f
--- /dev/null
+++ b/src/draw/Makefile
@@ -0,0 +1,4 @@
+BDF=5x7.bdf
+
+ao_font.h: font-convert $(BDF)
+ nickle font-convert $(BDF) > $@
diff --git a/src/draw/ao_blt.c b/src/draw/ao_blt.c
new file mode 100644
index 00000000..e3f45221
--- /dev/null
+++ b/src/draw/ao_blt.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+#define O 0
+#define I AO_ALLONES
+
+struct ao_merge_rop {
+ uint32_t ca1, cx1, ca2, cx2;
+};
+
+const struct ao_merge_rop ao_merge_rop[16] = {
+ {O, O, O, O}, /* clear 0x0 0 */
+ {I, O, O, O}, /* and 0x1 src AND dst */
+ {I, O, I, O}, /* andReverse 0x2 src AND NOT dst */
+ {O, O, I, O}, /* copy 0x3 src */
+ {I, I, O, O}, /* andInverted 0x4 NOT src AND dst */
+ {O, I, O, O}, /* noop 0x5 dst */
+ {O, I, I, O}, /* xor 0x6 src XOR dst */
+ {I, I, I, O}, /* or 0x7 src OR dst */
+ {I, I, I, I}, /* nor 0x8 NOT src AND NOT dst */
+ {O, I, I, I}, /* equiv 0x9 NOT src XOR dst */
+ {O, I, O, I}, /* invert 0xa NOT dst */
+ {I, I, O, I}, /* orReverse 0xb src OR NOT dst */
+ {O, O, I, I}, /* copyInverted 0xc NOT src */
+ {I, O, I, I}, /* orInverted 0xd NOT src OR dst */
+ {I, O, O, I}, /* nand 0xe NOT src OR NOT dst */
+ {O, O, O, I}, /* set 0xf 1 */
+};
+
+#define ao_do_merge_rop(src, dst) \
+ (((dst) & (((src) & _ca1) ^ _cx1)) ^ (((src) & _ca2) ^ _cx2))
+
+#define ao_do_dst_invarient_merge_rop(src) (((src) & _ca2) ^ _cx2)
+
+#define ao_do_mask_merge_rop(src, dst, mask) \
+ (((dst) & ((((src) & _ca1) ^ _cx1) | ~(mask))) ^ ((((src) & _ca2) ^ _cx2) & (mask)))
+
+#define ao_dst_invarient_merge_rop() (_ca1 == 0 && _cx1 == 0)
+
+void
+ao_blt(uint32_t *src_line,
+ int16_t src_stride,
+ int16_t src_x,
+ uint32_t *dst_line,
+ int16_t dst_stride,
+ int16_t dst_x,
+ int16_t width,
+ int16_t height,
+ uint8_t rop,
+ uint8_t reverse,
+ uint8_t upsidedown)
+{
+ uint32_t *src, *dst;
+ uint32_t _ca1, _cx1, _ca2, _cx2;
+ uint8_t dst_invarient;
+ uint32_t startmask, endmask;
+ int16_t nmiddle, n;
+ uint32_t bits1, bits;
+ int16_t left_shift, right_shift;
+
+ _ca1 = ao_merge_rop[rop].ca1;
+ _cx1 = ao_merge_rop[rop].cx1;
+ _ca2 = ao_merge_rop[rop].ca2;
+ _cx2 = ao_merge_rop[rop].cx2;
+ dst_invarient = ao_dst_invarient_merge_rop();
+
+ if (upsidedown) {
+ src_line += (height - 1) * src_stride;
+ dst_line += (height - 1) * dst_stride;
+ src_stride = -src_stride;
+ dst_stride = -dst_stride;
+ }
+
+ ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
+ if (reverse) {
+ src_line += ((src_x + width - 1) >> AO_SHIFT) + 1;
+ dst_line += ((dst_x + width - 1) >> AO_SHIFT) + 1;
+ src_x = (src_x + width - 1) & AO_MASK;
+ dst_x = (dst_x + width - 1) & AO_MASK;
+ } else {
+ src_line += src_x >> AO_SHIFT;
+ dst_line += dst_x >> AO_SHIFT;
+ src_x &= AO_MASK;
+ dst_x &= AO_MASK;
+ }
+ if (src_x == dst_x) {
+ while (height--) {
+ src = src_line;
+ src_line += src_stride;
+ dst = dst_line;
+ dst_line += dst_stride;
+ if (reverse) {
+ if (endmask) {
+ bits = *--src;
+ --dst;
+ *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+ }
+ n = nmiddle;
+ if (dst_invarient) {
+ while (n--)
+ *--dst = ao_do_dst_invarient_merge_rop(*--src);
+ }
+ else {
+ while (n--) {
+ bits = *--src;
+ --dst;
+ *dst = ao_do_merge_rop(bits, *dst);
+ }
+ }
+ if (startmask) {
+ bits = *--src;
+ --dst;
+ *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+ }
+ }
+ else {
+ if (startmask) {
+ bits = *src++;
+ *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+ dst++;
+ }
+ n = nmiddle;
+ if (dst_invarient) {
+ while (n--)
+ *dst++ = ao_do_dst_invarient_merge_rop(*src++);
+ }
+ else {
+ while (n--) {
+ bits = *src++;
+ *dst = ao_do_merge_rop(bits, *dst);
+ dst++;
+ }
+ }
+ if (endmask) {
+ bits = *src;
+ *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+ }
+ }
+ }
+ } else {
+ if (src_x > dst_x) {
+ left_shift = src_x - dst_x;
+ right_shift = AO_UNIT - left_shift;
+ } else {
+ right_shift = dst_x - src_x;
+ left_shift = AO_UNIT - right_shift;
+ }
+ while (height--) {
+ src = src_line;
+ src_line += src_stride;
+ dst = dst_line;
+ dst_line += dst_stride;
+
+ bits1 = 0;
+ if (reverse) {
+ if (src_x < dst_x)
+ bits1 = *--src;
+ if (endmask) {
+ bits = ao_right(bits1, right_shift);
+ if (ao_right(endmask, left_shift)) {
+ bits1 = *--src;
+ bits |= ao_left(bits1, left_shift);
+ }
+ --dst;
+ *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+ }
+ n = nmiddle;
+ if (dst_invarient) {
+ while (n--) {
+ bits = ao_right(bits1, right_shift);
+ bits1 = *--src;
+ bits |= ao_left(bits1, left_shift);
+ --dst;
+ *dst = ao_do_dst_invarient_merge_rop(bits);
+ }
+ } else {
+ while (n--) {
+ bits = ao_right(bits1, right_shift);
+ bits1 = *--src;
+ bits |= ao_left(bits1, left_shift);
+ --dst;
+ *dst = ao_do_merge_rop(bits, *dst);
+ }
+ }
+ if (startmask) {
+ bits = ao_right(bits1, right_shift);
+ if (ao_right(startmask, left_shift)) {
+ bits1 = *--src;
+ bits |= ao_left(bits1, left_shift);
+ }
+ --dst;
+ *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+ }
+ }
+ else {
+ if (src_x > dst_x)
+ bits1 = *src++;
+ if (startmask) {
+ bits = ao_left(bits1, left_shift);
+ if (ao_left(startmask, right_shift)) {
+ bits1 = *src++;
+ bits |= ao_right(bits1, right_shift);
+ }
+ *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+ dst++;
+ }
+ n = nmiddle;
+ if (dst_invarient) {
+ while (n--) {
+ bits = ao_left(bits1, left_shift);
+ bits1 = *src++;
+ bits |= ao_right(bits1, right_shift);
+ *dst = ao_do_dst_invarient_merge_rop(bits);
+ dst++;
+ }
+ }
+ else {
+ while (n--) {
+ bits = ao_left(bits1, left_shift);
+ bits1 = *src++;
+ bits |= ao_right(bits1, right_shift);
+ *dst = ao_do_merge_rop(bits, *dst);
+ dst++;
+ }
+ }
+ if (endmask) {
+ bits = ao_left(bits1, left_shift);
+ if (ao_left(endmask, right_shift)) {
+ bits1 = *src;
+ bits |= ao_right(bits1, right_shift);
+ }
+ *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+ }
+ }
+ }
+ }
+}
+
+void
+ao_solid(uint32_t and,
+ uint32_t xor,
+ uint32_t *dst,
+ int16_t dst_stride,
+ int16_t dst_x,
+ int16_t width,
+ int16_t height)
+{
+ uint32_t startmask, endmask;
+ int16_t nmiddle;
+ int16_t n;
+
+ dst += dst_x >> AO_SHIFT;
+ dst_x &= AO_MASK;
+
+ ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
+
+ if (startmask)
+ dst_stride--;
+
+ dst_stride -= nmiddle;
+ while (height--) {
+ if (startmask) {
+ *dst = ao_do_mask_rrop(*dst, and, xor, startmask);
+ dst++;
+ }
+ n = nmiddle;
+ if (!and)
+ while (n--)
+ *dst++ = xor;
+ else
+ while (n--) {
+ *dst = ao_do_rrop(*dst, and, xor);
+ dst++;
+ }
+ if (endmask)
+ *dst = ao_do_mask_rrop(*dst, and, xor, endmask);
+ dst += dst_stride;
+ }
+}
diff --git a/src/draw/ao_copy.c b/src/draw/ao_copy.c
new file mode 100644
index 00000000..47067bb8
--- /dev/null
+++ b/src/draw/ao_copy.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+#define bound(val,max,other) do { \
+ if (val < 0) { \
+ other -= val; \
+ val = 0; \
+ } \
+ if (val > max) { \
+ other -= (val - max); \
+ val = max; \
+ } \
+ } while (0)
+
+#define bound2(a, max_a, b, max_b) do { \
+ bound(a, max_a, b); \
+ bound(b, max_b, a); \
+ } while (0)
+
+void
+ao_copy(const struct ao_bitmap *dst,
+ int16_t dst_x,
+ int16_t dst_y,
+ int16_t width,
+ int16_t height,
+ const struct ao_bitmap *src,
+ int16_t src_x,
+ int16_t src_y,
+ uint8_t rop)
+{
+ int16_t dst_x2 = dst_x + width, dst_y2 = dst_y + height;
+ int16_t src_x2 = src_x + width, src_y2 = src_y + height;
+ uint8_t reverse = 0;
+ uint8_t upsidedown = 0;
+
+ bound2(dst_x, dst->width, src_x, src->width);
+ bound2(dst_x2, dst->width, src_x2, src->width);
+ bound2(dst_y, dst->height, src_y, src->height);
+ bound2(dst_y2, dst->height, src_y2, src->height);
+
+ if (dst == src) {
+ reverse = (dst_x > src_x);
+ upsidedown = (dst_y > src_y);
+ }
+
+ if (dst_x < dst_x2 && dst_y < dst_y2) {
+ ao_blt(src->base + src_y * src->stride,
+ src->stride,
+ src_x,
+ dst->base + dst_y * dst->stride,
+ dst->stride,
+ dst_x,
+ dst_x2 - dst_x,
+ dst_y2 - dst_y,
+ rop,
+ reverse,
+ upsidedown);
+ }
+}
+
diff --git a/src/draw/ao_draw.h b/src/draw/ao_draw.h
new file mode 100644
index 00000000..92150fc1
--- /dev/null
+++ b/src/draw/ao_draw.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_DRAW_H_
+#define _AO_DRAW_H_
+
+struct ao_bitmap {
+ uint32_t *base;
+ int16_t stride; /* in units */
+ int16_t width; /* in pixels */
+ int16_t height; /* in pixels */
+};
+
+struct ao_pattern {
+ uint8_t pattern[8];
+};
+
+void
+ao_copy(const struct ao_bitmap *dst,
+ int16_t dst_x,
+ int16_t dst_y,
+ int16_t width,
+ int16_t height,
+ const struct ao_bitmap *src,
+ int16_t src_x,
+ int16_t src_y,
+ uint8_t rop);
+
+void
+ao_rect(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ int16_t width,
+ int16_t height,
+ uint32_t fill,
+ uint8_t rop);
+
+void
+ao_pattern(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ int16_t width,
+ int16_t height,
+ const struct ao_pattern *pattern,
+ int16_t pat_x,
+ int16_t pat_y,
+ uint8_t rop);
+
+void
+ao_line(const struct ao_bitmap *dst,
+ int16_t x1,
+ int16_t y1,
+ int16_t x2,
+ int16_t y2,
+ uint32_t fill,
+ uint8_t rop);
+
+void
+ao_text(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ char *string,
+ uint32_t fill,
+ uint8_t rop);
+
+struct ao_font {
+ int width;
+ int height;
+ int ascent;
+ int descent;
+};
+
+extern const struct ao_font ao_font;
+
+#define AO_SHIFT 5
+#define AO_UNIT (1 << AO_SHIFT)
+#define AO_MASK (AO_UNIT - 1)
+#define AO_ALLONES ((uint32_t) -1)
+
+/*
+ * dst
+ * 0 1
+ *
+ * 0 a b
+ * src
+ * 1 c d
+ *
+ * ROP = abcd
+ */
+
+#define AO_CLEAR 0x0 /* 0 */
+#define AO_AND 0x1 /* src AND dst */
+#define AO_AND_REVERSE 0x2 /* src AND NOT dst */
+#define AO_COPY 0x3 /* src */
+#define AO_AND_INVERTED 0x4 /* NOT src AND dst */
+#define AO_NOOP 0x5 /* dst */
+#define AO_XOR 0x6 /* src XOR dst */
+#define AO_OR 0x7 /* src OR dst */
+#define AO_NOR 0x8 /* NOT src AND NOT dst */
+#define AO_EQUIV 0x9 /* NOT src XOR dst */
+#define AO_INVERT 0xa /* NOT dst */
+#define AO_OR_REVERSE 0xb /* src OR NOT dst */
+#define AO_COPY_INVERTED 0xc /* NOT src */
+#define AO_OR_INVERTED 0xd /* NOT src OR dst */
+#define AO_NAND 0xe /* NOT src OR NOT dst */
+#define AO_SET 0xf /* 1 */
+
+#endif /* _AO_DRAW_H_ */
diff --git a/src/draw/ao_draw_int.h b/src/draw/ao_draw_int.h
new file mode 100644
index 00000000..433aa409
--- /dev/null
+++ b/src/draw/ao_draw_int.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_DRAW_INT_H_
+#define _AO_DRAW_INT_H_
+
+static inline uint32_t
+ao_expand(uint32_t bits)
+{
+ return ~((bits & 1)-1);
+}
+
+static inline uint32_t
+ao_xor(uint8_t rop, uint32_t fg)
+{
+ fg = ao_expand(fg);
+
+ return (fg & ao_expand(rop >> 1)) |
+ (~fg & ao_expand(rop >> 3));
+}
+
+static inline uint32_t
+ao_and(uint8_t rop, uint32_t fg)
+{
+ fg = ao_expand(fg);
+
+ return (fg & ao_expand(rop ^ (rop >> 1))) |
+ (~fg & ao_expand((rop>>2) ^ (rop>>3)));
+}
+
+static inline uint32_t
+ao_left(uint32_t bits, int16_t shift) {
+ return bits >> shift;
+}
+
+static inline uint32_t
+ao_right(uint32_t bits, int16_t shift) {
+ return bits << shift;
+}
+
+static inline uint32_t
+ao_right_mask(int16_t x) {
+ if ((AO_UNIT - x) & AO_MASK)
+ return ao_left(AO_ALLONES,(AO_UNIT - x) & AO_MASK);
+ else
+ return 0;
+}
+
+static inline uint32_t
+ao_left_mask(int16_t x) {
+ if (x & AO_MASK)
+ return ao_right(AO_ALLONES, x & AO_MASK);
+ else
+ return 0;
+}
+
+static inline uint32_t
+ao_bits_mask(int16_t x, int16_t w) {
+ return ao_right(AO_ALLONES, x & AO_MASK) &
+ ao_left(AO_ALLONES,(AO_UNIT - (x + w)) & AO_MASK);
+}
+
+#define ao_mask_bits(x,w,l,n,r) { \
+ n = (w); \
+ r = ao_right_mask((x)+n); \
+ l = ao_left_mask(x); \
+ if (l) { \
+ n -= AO_UNIT - ((x) & AO_MASK); \
+ if (n < 0) { \
+ n = 0; \
+ l &= r; \
+ r = 0; \
+ } \
+ } \
+ n >>= AO_SHIFT; \
+}
+
+#define ao_clip(val,min,max) do { \
+ if (val < min) { \
+ val = min; \
+ } else if (val > max) { \
+ val = max; \
+ } \
+ } while (0)
+
+static inline uint32_t
+ao_do_mask_rrop(uint32_t dst, uint32_t and, uint32_t xor, uint32_t mask) {
+ return (dst & (and | ~mask)) ^ (xor & mask);
+}
+
+static inline uint32_t
+ao_do_rrop(uint32_t dst, uint32_t and, uint32_t xor) {
+ return (dst & and) ^ xor;
+}
+
+void
+ao_blt(uint32_t *src_line,
+ int16_t src_stride,
+ int16_t src_x,
+ uint32_t *dst_line,
+ int16_t dst_stride,
+ int16_t dst_x,
+ int16_t width,
+ int16_t height,
+ uint8_t rop,
+ uint8_t reverse,
+ uint8_t upsidedown);
+
+void
+ao_solid(uint32_t and,
+ uint32_t xor,
+ uint32_t *dst,
+ int16_t dst_stride,
+ int16_t dst_x,
+ int16_t width,
+ int16_t height);
+
+int16_t
+ao_glyph(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ uint8_t c,
+ uint8_t rop);
+
+#endif /* _AO_DRAW_INT_H_ */
diff --git a/src/draw/ao_font.h b/src/draw/ao_font.h
new file mode 100644
index 00000000..5e31dd11
--- /dev/null
+++ b/src/draw/ao_font.h
@@ -0,0 +1,139 @@
+static const uint8_t glyph_bytes[1568] = {
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x0e, 0x04, 0x00, 0x0a, 0x05,
+ 0x0a, 0x05, 0x0a, 0x05, 0x00, 0x05, 0x07, 0x05, 0x05, 0x0e, 0x04, 0x04, 0x03, 0x01, 0x03, 0x0d,
+ 0x04, 0x0c, 0x04, 0x03, 0x01, 0x03, 0x06, 0x0a, 0x06, 0x0a, 0x01, 0x01, 0x03, 0x0c, 0x04, 0x0c,
+ 0x04, 0x04, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0e, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x09,
+ 0x0b, 0x0d, 0x09, 0x04, 0x04, 0x0c, 0x05, 0x05, 0x05, 0x02, 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x04,
+ 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1f, 0x00, 0x04, 0x04, 0x04, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x02, 0x04, 0x08, 0x0e, 0x00, 0x02, 0x04, 0x08,
+ 0x04, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x08, 0x0e, 0x04, 0x0e,
+ 0x02, 0x00, 0x00, 0x0c, 0x02, 0x07, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04, 0x00, 0x0a, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x00, 0x00, 0x0e, 0x05, 0x0e,
+ 0x14, 0x0e, 0x00, 0x01, 0x09, 0x04, 0x02, 0x09, 0x08, 0x00, 0x00, 0x02, 0x05, 0x02, 0x05, 0x0a,
+ 0x00, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, 0x04, 0x00, 0x02,
+ 0x04, 0x04, 0x04, 0x04, 0x02, 0x00, 0x00, 0x05, 0x02, 0x07, 0x02, 0x05, 0x00, 0x00, 0x04, 0x04,
+ 0x1f, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00,
+ 0x02, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x02, 0x03, 0x02, 0x02, 0x02, 0x07, 0x00, 0x06, 0x09,
+ 0x08, 0x04, 0x02, 0x0f, 0x00, 0x0f, 0x08, 0x06, 0x08, 0x09, 0x06, 0x00, 0x04, 0x06, 0x05, 0x0f,
+ 0x04, 0x04, 0x00, 0x0f, 0x01, 0x07, 0x08, 0x09, 0x06, 0x00, 0x06, 0x01, 0x07, 0x09, 0x09, 0x06,
+ 0x00, 0x0f, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x06, 0x09, 0x06, 0x09, 0x09, 0x06, 0x00, 0x06,
+ 0x09, 0x09, 0x0e, 0x08, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06,
+ 0x00, 0x06, 0x02, 0x01, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x02, 0x01, 0x00, 0x02, 0x05, 0x04, 0x02, 0x00, 0x02, 0x00,
+ 0x06, 0x09, 0x0d, 0x0d, 0x01, 0x06, 0x00, 0x06, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00, 0x07, 0x09,
+ 0x07, 0x09, 0x09, 0x07, 0x00, 0x06, 0x09, 0x01, 0x01, 0x09, 0x06, 0x00, 0x07, 0x09, 0x09, 0x09,
+ 0x09, 0x07, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x01,
+ 0x00, 0x06, 0x09, 0x01, 0x0d, 0x09, 0x0e, 0x00, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x09, 0x00, 0x07,
+ 0x02, 0x02, 0x02, 0x02, 0x07, 0x00, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06, 0x00, 0x09, 0x05, 0x03,
+ 0x03, 0x05, 0x09, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0f, 0x00, 0x09, 0x0f, 0x0f, 0x09, 0x09,
+ 0x09, 0x00, 0x09, 0x0b, 0x0b, 0x0d, 0x0d, 0x09, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00,
+ 0x07, 0x09, 0x09, 0x07, 0x01, 0x01, 0x00, 0x06, 0x09, 0x09, 0x09, 0x0b, 0x06, 0x08, 0x07, 0x09,
+ 0x09, 0x07, 0x05, 0x09, 0x00, 0x06, 0x09, 0x02, 0x04, 0x09, 0x06, 0x00, 0x07, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x09, 0x09, 0x09, 0x09, 0x06, 0x06,
+ 0x00, 0x09, 0x09, 0x09, 0x0f, 0x0f, 0x09, 0x00, 0x09, 0x09, 0x06, 0x06, 0x09, 0x09, 0x00, 0x05,
+ 0x05, 0x05, 0x02, 0x02, 0x02, 0x00, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x0f, 0x00, 0x07, 0x01, 0x01,
+ 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04,
+ 0x07, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x03, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x01, 0x01,
+ 0x07, 0x09, 0x09, 0x07, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x06, 0x00, 0x08, 0x08, 0x0e, 0x09,
+ 0x09, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x03, 0x06, 0x00, 0x04, 0x0a, 0x02, 0x07, 0x02, 0x02,
+ 0x00, 0x00, 0x00, 0x0e, 0x09, 0x06, 0x01, 0x0e, 0x01, 0x01, 0x07, 0x09, 0x09, 0x09, 0x00, 0x02,
+ 0x00, 0x03, 0x02, 0x02, 0x07, 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x05, 0x02, 0x01, 0x01, 0x05,
+ 0x03, 0x05, 0x09, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x07, 0x00, 0x00, 0x00, 0x05, 0x0f, 0x09,
+ 0x09, 0x00, 0x00, 0x00, 0x07, 0x09, 0x09, 0x09, 0x00, 0x00, 0x00, 0x06, 0x09, 0x09, 0x06, 0x00,
+ 0x00, 0x00, 0x07, 0x09, 0x09, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x09, 0x09, 0x0e, 0x08, 0x00, 0x00,
+ 0x07, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x03, 0x0c, 0x07, 0x00, 0x02, 0x02, 0x07, 0x02,
+ 0x02, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x09, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x02,
+ 0x00, 0x00, 0x00, 0x09, 0x09, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x09, 0x06, 0x06, 0x09, 0x00, 0x00,
+ 0x00, 0x09, 0x09, 0x0a, 0x04, 0x02, 0x00, 0x00, 0x0f, 0x04, 0x02, 0x0f, 0x00, 0x04, 0x02, 0x03,
+ 0x02, 0x02, 0x04, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x06, 0x02, 0x02,
+ 0x01, 0x00, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04,
+ 0x0e, 0x05, 0x05, 0x0e, 0x04, 0x00, 0x0c, 0x02, 0x07, 0x02, 0x0d, 0x00, 0x00, 0x11, 0x0e, 0x0a,
+ 0x0e, 0x11, 0x00, 0x05, 0x05, 0x02, 0x07, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04,
+ 0x00, 0x0c, 0x02, 0x06, 0x0a, 0x0c, 0x08, 0x06, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e,
+ 0x11, 0x15, 0x13, 0x15, 0x11, 0x0e, 0x06, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
+ 0x09, 0x12, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x0e, 0x11, 0x17, 0x13, 0x13, 0x11, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x1f, 0x00, 0x06, 0x04,
+ 0x02, 0x06, 0x00, 0x00, 0x00, 0x06, 0x06, 0x04, 0x06, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x09, 0x07, 0x01, 0x0e, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x04,
+ 0x06, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x12, 0x09, 0x00, 0x00, 0x01, 0x01, 0x01, 0x09, 0x0c, 0x0e, 0x08, 0x01, 0x01, 0x01, 0x0d, 0x08,
+ 0x04, 0x0c, 0x03, 0x03, 0x02, 0x0b, 0x0c, 0x0e, 0x08, 0x02, 0x00, 0x02, 0x01, 0x05, 0x02, 0x00,
+ 0x06, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00, 0x06, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00, 0x06, 0x09,
+ 0x09, 0x0f, 0x09, 0x09, 0x00, 0x06, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00, 0x06, 0x09, 0x09, 0x0f,
+ 0x09, 0x09, 0x00, 0x06, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00, 0x0e, 0x05, 0x0d, 0x07, 0x05, 0x0d,
+ 0x00, 0x06, 0x09, 0x01, 0x01, 0x09, 0x06, 0x02, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f, 0x00, 0x0f,
+ 0x01, 0x07, 0x01, 0x01, 0x0f, 0x00, 0x0f, 0x01, 0x07, 0x01, 0x01, 0x0f, 0x00, 0x0f, 0x01, 0x07,
+ 0x01, 0x01, 0x0f, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x07, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02,
+ 0x07, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x07, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x07, 0x00,
+ 0x07, 0x0a, 0x0b, 0x0a, 0x0a, 0x07, 0x00, 0x0d, 0x09, 0x0b, 0x0d, 0x0d, 0x09, 0x00, 0x06, 0x09,
+ 0x09, 0x09, 0x09, 0x06, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x06, 0x09, 0x09, 0x09,
+ 0x09, 0x06, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06,
+ 0x00, 0x00, 0x00, 0x09, 0x06, 0x06, 0x09, 0x00, 0x0e, 0x0d, 0x0d, 0x0b, 0x0b, 0x07, 0x00, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x06, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x00, 0x05, 0x05, 0x05, 0x02, 0x02,
+ 0x02, 0x00, 0x01, 0x07, 0x09, 0x07, 0x01, 0x01, 0x00, 0x06, 0x09, 0x07, 0x09, 0x0b, 0x05, 0x01,
+ 0x02, 0x04, 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x04, 0x02, 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x04, 0x0a,
+ 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x0a, 0x05, 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x0a, 0x00, 0x0e, 0x09,
+ 0x0d, 0x0a, 0x00, 0x06, 0x06, 0x0e, 0x09, 0x0d, 0x0a, 0x00, 0x00, 0x00, 0x0e, 0x0d, 0x05, 0x0e,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x06, 0x02, 0x02, 0x04, 0x06, 0x0d, 0x03, 0x06, 0x00, 0x04,
+ 0x02, 0x06, 0x0d, 0x03, 0x06, 0x00, 0x02, 0x05, 0x06, 0x0d, 0x03, 0x06, 0x00, 0x05, 0x00, 0x06,
+ 0x0d, 0x03, 0x06, 0x00, 0x01, 0x02, 0x03, 0x02, 0x02, 0x07, 0x00, 0x02, 0x01, 0x03, 0x02, 0x02,
+ 0x07, 0x00, 0x02, 0x05, 0x03, 0x02, 0x02, 0x07, 0x00, 0x05, 0x00, 0x03, 0x02, 0x02, 0x07, 0x00,
+ 0x02, 0x0c, 0x06, 0x09, 0x09, 0x06, 0x00, 0x0a, 0x05, 0x07, 0x09, 0x09, 0x09, 0x00, 0x02, 0x04,
+ 0x06, 0x09, 0x09, 0x06, 0x00, 0x04, 0x02, 0x06, 0x09, 0x09, 0x06, 0x00, 0x06, 0x00, 0x06, 0x09,
+ 0x09, 0x06, 0x00, 0x0a, 0x05, 0x06, 0x09, 0x09, 0x06, 0x00, 0x05, 0x00, 0x06, 0x09, 0x09, 0x06,
+ 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x0d, 0x0b, 0x07, 0x00, 0x02,
+ 0x04, 0x09, 0x09, 0x09, 0x0e, 0x00, 0x04, 0x02, 0x09, 0x09, 0x09, 0x0e, 0x00, 0x06, 0x00, 0x09,
+ 0x09, 0x09, 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x09, 0x09, 0x0e, 0x00, 0x04, 0x02, 0x09, 0x09, 0x0a,
+ 0x04, 0x02, 0x00, 0x01, 0x07, 0x09, 0x09, 0x07, 0x01, 0x0a, 0x00, 0x09, 0x09, 0x0a, 0x04, 0x02,
+};
+
+static const uint16_t glyph_pos[256] = {
+ 0, 7, 14, 21, 28, 35, 42, 49,
+ 56, 63, 70, 77, 84, 91, 98, 105,
+ 112, 119, 126, 133, 140, 147, 154, 161,
+ 168, 175, 182, 189, 196, 203, 210, 217,
+ 224, 231, 238, 245, 252, 259, 266, 273,
+ 280, 287, 294, 301, 308, 315, 322, 329,
+ 336, 343, 350, 357, 364, 371, 378, 385,
+ 392, 399, 406, 413, 420, 427, 434, 441,
+ 448, 455, 462, 469, 476, 483, 490, 497,
+ 504, 511, 518, 525, 532, 539, 546, 553,
+ 560, 567, 574, 581, 588, 595, 602, 609,
+ 616, 623, 630, 637, 644, 651, 658, 665,
+ 672, 679, 686, 693, 700, 707, 714, 721,
+ 728, 735, 742, 749, 756, 763, 770, 777,
+ 784, 791, 798, 805, 812, 819, 826, 833,
+ 840, 847, 854, 861, 868, 875, 882, 889,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 896, 903, 910, 917, 924, 931, 938, 945,
+ 952, 959, 966, 973, 980, 987, 994, 1001,
+ 1008, 1015, 1022, 1029, 1036, 1043, 1050, 1057,
+ 1064, 1071, 1078, 1085, 1092, 1099, 1106, 1113,
+ 1120, 1127, 1134, 1141, 1148, 1155, 1162, 1169,
+ 1176, 1183, 1190, 1197, 1204, 1211, 1218, 1225,
+ 1232, 1239, 1246, 1253, 1260, 1267, 1274, 1281,
+ 1288, 1295, 1302, 1309, 1316, 1323, 1330, 1337,
+ 1344, 1351, 1358, 1365, 1372, 1379, 1386, 1393,
+ 1400, 1407, 1414, 1421, 1428, 1435, 1442, 1449,
+ 1456, 1463, 1470, 1477, 1484, 1491, 1498, 1505,
+ 1512, 1519, 1526, 1533, 1540, 1547, 1554, 1561,
+};
+
+#define GLYPH_WIDTH 5
+#define GLYPH_HEIGHT 7
+#define GLYPH_ASCENT 6
diff --git a/src/draw/ao_line.c b/src/draw/ao_line.c
new file mode 100644
index 00000000..ed1fc21c
--- /dev/null
+++ b/src/draw/ao_line.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+#define ao_mask(x,w) (ao_right(AO_ALLONES,(x) & AO_MASK) & \
+ ao_left(AO_ALLONES,(FB_UNIT - ((x)+(w))) & AO_MASK))
+
+
+/* out of clip region codes */
+#define OUT_LEFT 0x08
+#define OUT_RIGHT 0x04
+#define OUT_ABOVE 0x02
+#define OUT_BELOW 0x01
+
+/* major axis for bresenham's line */
+#define X_AXIS 0
+#define Y_AXIS 1
+
+/*
+ * Line clipping. Clip to the box, bringing the coordinates forward while
+ * preserving the actual slope and error
+ *
+ *
+ * X major line, clip X:
+ *
+ * adjust_x = -x;
+ *
+ * e += adjust_x * e1;
+ *
+ * adjust_y = (e + -e3-1) / -e3;
+ *
+ * e -= adjust_y / -e3;
+ *
+ * X major line, clip Y:
+ *
+ * adjust_y = -y;
+
+ *
+ * e -= adjust_y / -e3;
+ *
+ * adjust_x = e / e1;
+ */
+
+
+
+
+static void
+ao_bres(const struct ao_bitmap *dst_bitmap,
+ int16_t signdx,
+ int16_t signdy,
+ int16_t axis,
+ int16_t x1,
+ int16_t y1,
+ int16_t e,
+ int16_t e1,
+ int16_t e3,
+ int16_t len,
+ uint32_t and,
+ uint32_t xor)
+{
+ int16_t stride = dst_bitmap->stride;
+ uint32_t *dst = dst_bitmap->base;
+ uint32_t mask0, mask;
+
+ mask0 = 1;
+ if (signdx < 0)
+ mask0 = ao_right(1, AO_UNIT - 1);
+
+ if (signdy < 0)
+ stride = -stride;
+
+ dst = dst + y1 * stride + (x1 >> AO_SHIFT);
+ mask = ao_right(1, x1 & AO_MASK);
+
+ while (len--) {
+ /* clip each point */
+
+ *dst = ao_do_mask_rrop(*dst, and, xor, mask);
+
+ if (axis == X_AXIS) {
+ if (signdx < 0)
+ mask = ao_left(mask, 1);
+ else
+ mask = ao_right(mask, 1);
+ if (!mask) {
+ dst += signdx;
+ mask = mask0;
+ }
+ e += e1;
+ if (e >= 0) {
+ dst += stride;
+ e += e3;
+ }
+ } else {
+ dst += stride;
+ e += e1;
+ if (e >= 0) {
+ if (signdx < 0)
+ mask = ao_left(mask, 1);
+ else
+ mask = ao_right(mask, 1);
+ if (!mask) {
+ dst += signdx;
+ mask = mask0;
+ }
+ e += e3;
+ }
+ }
+ }
+}
+
+struct ao_cc {
+ int16_t major;
+ int16_t minor;
+ int16_t sign_major;
+ int16_t sign_minor;
+ int16_t e;
+ int16_t e1;
+ int16_t e3;
+ int8_t first;
+};
+
+/* line clipping box */
+struct ao_cbox {
+ int16_t maj1, min1;
+ int16_t maj2, min2;
+};
+
+/* -b <= a, so we need to make a bigger */
+static int16_t
+div_ceil(int32_t a, int16_t b) {
+ return (a + b + b - 1) / b - 1;
+}
+
+static int16_t
+div_floor_plus_one(int32_t a, int16_t b) {
+ return (a + b) / b;
+}
+
+static int8_t
+ao_clip_line(struct ao_cc *c, struct ao_cbox *b)
+{
+ int32_t adjust_major = 0, adjust_minor = 0;
+
+ /* Clip major axis */
+ if (c->major < b->maj1) {
+ if (c->sign_major <= 0)
+ return FALSE;
+ adjust_major = b->maj1 - c->major;
+ } else if (c->major >= b->maj2) {
+ if (c->sign_major >= 0)
+ return FALSE;
+ adjust_major = c->major - (b->maj2-1);
+ }
+
+ /* Clip minor axis */
+ if (c->minor < b->min1) {
+ if (c->sign_minor <= 0)
+ return FALSE;
+ adjust_minor = b->min1 - c->minor;
+ } else if (c->minor >= b->min2) {
+ if (c->sign_minor >= 0)
+ return FALSE;
+ adjust_minor = c->minor - (b->min2-1);
+ }
+
+ /* If unclipped, we're done */
+ if (adjust_major == 0 && adjust_minor == 0)
+ return TRUE;
+
+ /* See how much minor adjustment would happen during
+ * a major clip. This is a bit tricky because line drawing
+ * isn't symmetrical when the line passes exactly between
+ * two pixels, we have to pick which one gets drawn
+ */
+ int32_t adj_min;
+
+ if (!c->first)
+ adj_min = div_ceil(c->e + adjust_major * c->e1, -c->e3);
+ else
+ adj_min = div_floor_plus_one(c->e + adjust_major * c->e1, -c->e3);
+
+ if (adj_min < adjust_minor) {
+ if (c->first)
+ adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
+ else
+ adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+ } else {
+ adjust_minor = adj_min;
+ }
+
+ c->e += adjust_major * c->e1 + adjust_minor * c->e3;
+
+ c->major += c->sign_major * adjust_major;
+ c->minor += c->sign_minor * adjust_minor;
+
+ return TRUE;
+}
+
+void
+ao_line(const struct ao_bitmap *dst,
+ int16_t x1,
+ int16_t y1,
+ int16_t x2,
+ int16_t y2,
+ uint32_t fill,
+ uint8_t rop)
+{
+ int16_t adx, ady;
+ int16_t e, e1, e2, e3;
+ int16_t signdx = 1, signdy = 1;
+ int16_t axis;
+ int16_t len;
+ struct ao_cc clip_1, clip_2;
+ struct ao_cbox cbox;
+
+ if ((adx = x2 - x1) < 0) {
+ adx = -adx;
+ signdx = -1;
+ }
+ if ((ady = y2 - y1) < 0) {
+ ady = -ady;
+ signdy = -1;
+ }
+
+ if (adx > ady) {
+ axis = X_AXIS;
+ e1 = ady << 1;
+ e2 = e1 - (adx << 1);
+ e = e1 - adx;
+
+ clip_1.major = x1;
+ clip_1.minor = y1;
+ clip_2.major = x2;
+ clip_2.minor = y2;
+ clip_1.sign_major = signdx;
+ clip_1.sign_minor = signdy;
+
+ cbox.maj1 = 0;
+ cbox.maj2 = dst->width;
+ cbox.min1 = 0;
+ cbox.min2 = dst->height;
+ } else {
+ axis = Y_AXIS;
+ e1 = adx << 1;
+ e2 = e1 - (ady << 1);
+ e = e1 - ady;
+
+ clip_1.major = y1;
+ clip_1.minor = x1;
+ clip_2.major = y2;
+ clip_2.minor = x2;
+ clip_1.sign_major = signdy;
+ clip_1.sign_minor = signdx;
+
+ cbox.maj1 = 0;
+ cbox.maj2 = dst->height;
+ cbox.min1 = 0;
+ cbox.min2 = dst->width;
+ }
+
+ e3 = e2 - e1;
+ e = e - e1;
+
+ clip_1.first = TRUE;
+ clip_2.first = FALSE;
+ clip_2.e = clip_1.e = e;
+ clip_2.e1 = clip_1.e1 = e1;
+ clip_2.e3 = clip_1.e3 = e3;
+ clip_2.sign_major = -clip_1.sign_major;
+ clip_2.sign_minor = -clip_1.sign_minor;
+
+ if (!ao_clip_line(&clip_1, &cbox))
+ return;
+
+ if (!ao_clip_line(&clip_2, &cbox))
+ return;
+
+ len = clip_1.sign_major * (clip_2.major - clip_1.major) + clip_2.first;
+
+ if (len <= 0)
+ return;
+
+ if (adx > ady) {
+ x1 = clip_1.major;
+ y1 = clip_1.minor;
+ } else {
+ x1 = clip_1.minor;
+ y1 = clip_1.major;
+ }
+ ao_bres(dst,
+ signdx,
+ signdy,
+ axis,
+ x1,
+ y1,
+ clip_1.e, e1, e3, len,
+ ao_and(rop, fill),
+ ao_xor(rop, fill));
+}
diff --git a/src/draw/ao_pattern.c b/src/draw/ao_pattern.c
new file mode 100644
index 00000000..0d1dc765
--- /dev/null
+++ b/src/draw/ao_pattern.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+static inline uint32_t
+ao_pattern_expand(uint8_t v, uint8_t rot)
+{
+ uint32_t r;
+
+ if (rot)
+ v = ao_left(v, 8-rot) | ao_right(v, rot);
+ r = v;
+ return (r << 24) | (r << 16) | (r << 8) | (r);
+}
+
+static inline int
+min(int a, int b) {
+ return a < b ? a : b;
+}
+
+void
+ao_pattern(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ int16_t width,
+ int16_t height,
+ const struct ao_pattern *pattern,
+ int16_t pat_x,
+ int16_t pat_y,
+ uint8_t rop)
+{
+ uint32_t pat[8];
+
+ int16_t x2 = x + width;
+ int16_t y2 = y + height;
+
+ ao_clip(x, 0, dst->width);
+ ao_clip(x2, 0, dst->width);
+ ao_clip(y, 0, dst->height);
+ ao_clip(y2, 0, dst->height);
+
+ if (x < x2 && y < y2) {
+ int xrot = (x - pat_x) & 7;
+ int yrot = (y - pat_y) & 7;
+ int i;
+ int16_t dst_x, dst_y;
+
+ for (i = 0; i < 8; i++)
+ pat[(i + yrot) & 7] = ao_pattern_expand(pattern->pattern[i], xrot);
+ for (dst_y = y; dst_y < y2; dst_y += 8) {
+ int h = min(y2 - dst_y, 8);
+ for (dst_x = x; dst_x < x2; dst_x += 8) {
+ int w = min(x2 - dst_x, 8);
+
+ ao_blt(pat, 1, 0,
+ dst->base + dst_y * dst->stride,
+ dst->stride,
+ dst_x,
+ w, h,
+ rop,
+ 0, 0);
+ }
+ }
+ }
+}
+
diff --git a/src/draw/ao_rect.c b/src/draw/ao_rect.c
new file mode 100644
index 00000000..71fa4aea
--- /dev/null
+++ b/src/draw/ao_rect.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+void
+ao_rect(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ int16_t width,
+ int16_t height,
+ uint32_t fill,
+ uint8_t rop)
+{
+ int16_t x2 = x + width;
+ int16_t y2 = y + height;
+
+ ao_clip(x, 0, dst->width);
+ ao_clip(x2, 0, dst->width);
+ ao_clip(y, 0, dst->height);
+ ao_clip(y2, 0, dst->height);
+
+ if (x < x2 && y < y2) {
+ ao_solid(ao_and(rop, fill),
+ ao_xor(rop, fill),
+ dst->base + y * dst->stride,
+ dst->stride,
+ x,
+ x2 - x,
+ y2 - y);
+ }
+}
+
diff --git a/src/draw/ao_text.c b/src/draw/ao_text.c
new file mode 100644
index 00000000..7ce2a623
--- /dev/null
+++ b/src/draw/ao_text.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+#include "ao_font.h"
+
+const struct ao_font ao_font = {
+ .width = GLYPH_WIDTH,
+ .height = GLYPH_HEIGHT,
+ .ascent = GLYPH_ASCENT,
+ .descent = GLYPH_HEIGHT - GLYPH_ASCENT,
+};
+
+void
+ao_text(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ char *string,
+ uint32_t fill,
+ uint8_t rop)
+{
+ uint32_t src[GLYPH_HEIGHT];
+ char c;
+ int h;
+
+ struct ao_bitmap src_bitmap = {
+ .base = src,
+ .stride = 1,
+ .width = GLYPH_WIDTH,
+ .height = GLYPH_HEIGHT
+ };
+
+ y -= GLYPH_ASCENT;
+
+ rop = (rop & 3) | 0x4;
+
+ if ((fill&1) == 0)
+ rop ^= 3;
+
+ while ((c = *string++)) {
+ const uint8_t *bytes = &glyph_bytes[glyph_pos[(uint8_t) c]];
+
+ for (h = 0; h < GLYPH_HEIGHT; h++)
+ src[h] = bytes[h];
+
+ ao_copy(dst,
+ x, y, GLYPH_WIDTH, GLYPH_HEIGHT,
+ &src_bitmap,
+ 0, 0, rop);
+ x += GLYPH_WIDTH;
+ }
+}
diff --git a/src/draw/font-convert b/src/draw/font-convert
new file mode 100755
index 00000000..1985e418
--- /dev/null
+++ b/src/draw/font-convert
@@ -0,0 +1,150 @@
+#!/usr/bin/nickle
+
+typedef struct {
+ int[] bytes;
+ int width;
+ int height;
+ int encoding;
+ int location;
+} glyph_t;
+
+typedef struct {
+ glyph_t[...] glyphs;
+ int default_char;
+ int ascent;
+} font_t;
+
+glyph_t
+read_glyph(file f)
+{
+ glyph_t glyph = { .encoding = -1, .bytes = (int[...]){}, .width = 0 };
+
+ while (!File::end(f)) {
+ string l = fgets(f);
+
+ string[*] tokens = String::split(l, " ");
+ if (dim(tokens) == 0)
+ continue;
+
+ switch (tokens[0]) {
+ case "ENCODING":
+ glyph.encoding = atoi(tokens[1]);
+ break;
+ case "DWIDTH":
+ glyph.width = atoi(tokens[1]);
+ break;
+ case "BBX":
+ glyph.height = atoi(tokens[2]);
+ break;
+ case "ENDCHAR":
+ return glyph;
+ case "BITMAP":
+ while (!File::end(f)) {
+ string l = fgets(f);
+ if (l == "ENDCHAR")
+ return glyph;
+ glyph.bytes[dim(glyph.bytes)] = atoi(l, 16);
+ }
+ break;
+ }
+ }
+ return glyph;
+}
+
+font_t read_font(file f) {
+ font_t font = { .glyphs = {}, .default_char = -1 };
+ bool in_head = true;
+
+ while (in_head && !File::end(f)) {
+ string l = File::fgets(f);
+
+ string[*] tokens = String::split(l, " ");
+ switch (tokens[0]) {
+ case "DEFAULT_CHAR":
+ font.default_char = atoi(tokens[1]);
+ break;
+ case "FONT_ASCENT":
+ font.ascent = atoi(tokens[1]);
+ break;
+ case "CHARS":
+ in_head = false;
+ break;
+ }
+ }
+ while (!File::end(f)) {
+ glyph_t glyph = read_glyph(f);
+ if (glyph.encoding == -1)
+ break;
+ font.glyphs[dim(font.glyphs)] = glyph;
+ }
+ return font;
+}
+
+int
+flip_byte(int x)
+{
+ int dest = 0;
+
+ for (int i = 0; i < 8; i++)
+ dest |= ((x >> (7 - i)) & 1) << i;
+ return dest;
+}
+
+void print_font(font_t font) {
+ int width = font.glyphs[0].width;
+ int height = font.glyphs[0].height;
+ int[256] pos = { -1 ... };
+ int[...] bytes;
+
+ if (false) {
+ for (int i = 1; i < dim(font.glyphs); i++) {
+ if (font.glyphs[i].width != width ||
+ font.glyphs[i].height != height)
+ {
+ File::fprintf(stderr, "font not constant size, glyph %d is %dx%d\n",
+ font.glyphs[i].encoding, font.glyphs[i].width, font.glyphs[i].height);
+ exit(1);
+ }
+ }
+ }
+
+ if (font.default_char == -1)
+ font.default_char = font.glyphs[0].encoding;
+
+ /* build byte array */
+ for (int i = 0; i < dim(font.glyphs); i++) {
+ pos[font.glyphs[i].encoding] = dim(bytes);
+ for (int b = 0; b < dim(font.glyphs[i].bytes); b++)
+ bytes[dim(bytes)] = font.glyphs[i].bytes[b];
+ }
+
+ /* Fill in default glyph */
+ for (int i = 0; i < dim(pos); i++)
+ if (pos[i] == -1)
+ pos[i] = pos[font.default_char];
+
+ printf("static const uint8_t glyph_bytes[%d] = {", dim(bytes));
+ for (int b = 0; b < dim(bytes); b++) {
+ if ((b & 15) == 0)
+ printf("\n\t");
+ printf("0x%02x, ", flip_byte(bytes[b]));
+ }
+ printf("\n};\n\n");
+
+ printf("static const uint16_t glyph_pos[%d] = {", dim(pos));
+ for (int i = 0; i < dim(pos); i++) {
+ if ((i & 7) == 0)
+ printf("\n\t");
+ printf("%4d, ", pos[i]);
+ }
+ printf("\n};\n\n");
+
+ printf("#define GLYPH_WIDTH %d\n", width);
+ printf("#define GLYPH_HEIGHT %d\n", height);
+ printf("#define GLYPH_ASCENT %d\n", font.ascent);
+}
+
+twixt (file f = File::open(argv[1], "r"); File::close(f)) {
+ font_t font = read_font(f);
+ print_font(font);
+}
diff --git a/src/draw/line.5c b/src/draw/line.5c
new file mode 100644
index 00000000..747768b0
--- /dev/null
+++ b/src/draw/line.5c
@@ -0,0 +1,389 @@
+#!/usr/bin/nickle
+
+autoimport Cairo;
+autoload PRNG;
+
+int
+sign(int x)
+{
+ return x == 0 ? 0 : x < 0 ? -1 : 1;
+}
+
+int X_AXIS = 0;
+int Y_AXIS = 1;
+
+typedef struct {
+ int major;
+ int minor;
+ int sign_major;
+ int sign_minor;
+ int e;
+ int e1;
+ int e3;
+ bool first;
+} clip_context;
+
+typedef struct {
+ int maj1, min1, maj2, min2;
+} clip_box;
+
+typedef struct {
+ int x1, y1, x2, y2;
+} box;
+
+typedef struct {
+ int x, y;
+} point;
+
+typedef struct {
+ int x1, y1, x2, y2;
+ box b;
+ point[] clipped;
+ point[] run;
+} test;
+
+box bounds = { .x1 = 10, .x2 = 30, .y1 = 10, .y2 = 30 };
+
+int
+div_ceil(a, b) {
+ a += b;
+ assert(a >= 0 && b > 0, "bad divide args %d %d\n", a, b);
+ return (a + b - 1) // b - 1;
+}
+
+int
+div_floor_plus_one(a, b) {
+ a += b;
+ assert(a >= 0 && b > 0, "bad divide args %d %d\n", a, b);
+ return a // b;
+}
+
+bool
+clip(*clip_context c, *clip_box b)
+{
+ int adjust_major = 0, adjust_minor = 0;
+
+ /* Clip major axis */
+ if (c->major < b->maj1) {
+ if (c->sign_major <= 0)
+ return false;
+ adjust_major = b->maj1 - c->major;
+ } else if (c->major >= b->maj2) {
+ if (c->sign_major >= 0)
+ return false;
+ adjust_major = c->major - (b->maj2-1);
+ }
+
+ /* Clip minor axis */
+ if (c->minor < b->min1) {
+ if (c->sign_minor <= 0)
+ return false;
+ adjust_minor = b->min1 - c->minor;
+ } else if (c->minor >= b->min2) {
+ if (c->sign_minor >= 0)
+ return false;
+ adjust_minor = c->minor - (b->min2-1);
+ }
+
+ /* If unclipped, we're done */
+ if (adjust_major == 0 && adjust_minor == 0)
+ return true;
+
+ /* See how much minor adjustment would happen during
+ * a major clip. This is a bit tricky because line drawing
+ * isn't symmetrical when the line passes exactly between
+ * two pixels, we have to pick which one gets drawn
+ */
+ int adj_min;
+
+ if (!c->first)
+ adj_min = div_ceil(c->e + adjust_major * c->e1, -c->e3);
+ else
+ adj_min = div_floor_plus_one(c->e + adjust_major * c->e1, -c->e3);
+
+ /* Compare that to the minor clip and pick
+ * the larger amount.
+ */
+ printf ("\tinitial major %d minor %d error %d e1 %d e3 %d\n", c->major, c->minor, c->e, c->e1, c->e3);
+
+ if (adj_min < adjust_minor) {
+ printf("\tminor clip dominates %d < %d. adjust major %d -> ",
+ adj_min, adjust_minor, adjust_major);
+ if (c->first)
+ adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
+ else
+ adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+ printf("%d\n", adjust_major);
+ } else {
+ printf("\tminor clip dominates %d > %d. adjust minor %d -> ",
+ adj_min, adjust_minor, adjust_minor);
+ adjust_minor = adj_min;
+ printf("%d\n", adjust_minor);
+ }
+
+ c->e += adjust_major * c->e1 + adjust_minor * c->e3;
+
+ c->major += c->sign_major * adjust_major;
+ c->minor += c->sign_minor * adjust_minor;
+
+ printf ("\tadjust major %d adjust minor %d e %d e1 %d e3 %e\n",
+ adjust_major, adjust_minor, c->e, c->e1, c->e3);
+
+ if (c->e >= 0)
+ printf ("error positive e %d e1 %d e3 %d\n",
+ c->e, c->e1, c->e3);
+ if (c->e < c->e3)
+ printf ("error magnitude too large e %d e1 %d e3 %d\n", c->e, c->e1, c->e3);
+
+ return true;
+}
+
+test
+line(int x1, int y1, int x2, int y2, *box b) {
+
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+ int signdx = sign(dx);
+ int signdy = sign(dy);
+ int adx = abs(dx);
+ int ady = abs(dy);
+ int axis;
+ int e, e1, e2, e3;
+ int len;
+ clip_context clip_1, clip_2;
+ clip_box c;
+ bool clipped = false;
+ test t = {
+ .x1 = x1,
+ .y1 = y1,
+ .x2 = x2,
+ .y2 = y2,
+ .b = *b,
+ .clipped = (point[...]) {},
+ .run = (point[...]) {}
+ };
+
+ if (adx >= ady) {
+ axis = X_AXIS;
+ e1 = ady << 1;
+ e2 = e1 - (adx << 1);
+ e = e1 - adx;
+ len = adx;
+
+ clip_1.major = x1;
+ clip_1.minor = y1;
+ clip_2.major = x2;
+ clip_2.minor = y2;
+ clip_1.sign_major = signdx;
+ clip_1.sign_minor = signdy;
+
+ c.maj1 = b->x1;
+ c.maj2 = b->x2;
+ c.min1 = b->y1;
+ c.min2 = b->y2;
+ } else {
+ axis = Y_AXIS;
+ e1 = adx << 1;
+ e2 = e1 - (ady << 1);
+ e = e1 - ady;
+ len = ady;
+
+ clip_1.major = y1;
+ clip_1.minor = x1;
+ clip_2.major = y2;
+ clip_2.minor = x2;
+ clip_1.sign_major = signdy;
+ clip_1.sign_minor = signdx;
+ c.maj1 = b->y1;
+ c.maj2 = b->y2;
+ c.min1 = b->x1;
+ c.min2 = b->x2;
+ }
+
+ e3 = e2 - e1;
+ e = e - e1;
+
+ clip_1.first = true;
+ clip_2.first = false;
+ clip_2.e = clip_1.e = e;
+ clip_2.e1 = clip_1.e1 = e1;
+ clip_2.e3 = clip_1.e3 = e3;
+ clip_2.sign_major = -clip_1.sign_major;
+ clip_2.sign_minor = -clip_1.sign_minor;
+
+ printf ("clip start:\n");
+ if (!clip(&clip_1, &c))
+ clipped = true;
+
+ printf("clip end:\n");
+ if (!clip(&clip_2, &c))
+ clipped = true;
+
+ int clip_len;
+ int clip_x, clip_y;
+ int clip_e;
+ int x_major, x_minor;
+ int y_major, y_minor;
+
+ clip_len = clip_1.sign_major * (clip_2.major - clip_1.major);
+ if (clip_len < 0)
+ clipped = true;
+
+ int x, y;
+
+ if (axis == X_AXIS) {
+ x = clip_1.major;
+ y = clip_1.minor;
+ x_major = clip_1.sign_major;
+ x_minor = 0;
+ y_major = 0;
+ y_minor = clip_1.sign_minor;
+ } else {
+ x = clip_1.minor;
+ y = clip_1.major;
+ x_major = 0;
+ x_minor = clip_1.sign_minor;
+ y_major = clip_1.sign_major;
+ y_minor = 0;
+ }
+
+ clip_e = clip_1.e;
+
+ if (clipped)
+ clip_len = -1;
+
+ while (clip_len-- >= 0) {
+ t.clipped[dim(t.clipped)] = (point) { .x = x, .y = y };
+ x += x_major;
+ y += y_major;
+ clip_e += e1;
+ if (clip_e >= 0) {
+ x += x_minor;
+ y += y_minor;
+ clip_e += e3;
+ }
+ }
+
+ x = x1;
+ y = y1;
+
+ while (len-- >= 0) {
+ if (bounds.x1 <= x && x < bounds.x2 &&
+ bounds.y1 <= y && y < bounds.y2) {
+ t.run[dim(t.run)] = (point) { .x = x, .y = y };
+ }
+ x += x_major;
+ y += y_major;
+ e += e1;
+ if (e >= 0) {
+ x += x_minor;
+ y += y_minor;
+ e += e3;
+ }
+ }
+ return t;
+}
+
+void read_events (Cairo::cairo_t cr)
+{
+ file event = Cairo::open_event(cr);
+
+ while (!File::end(event)) {
+ string event_line = File::fgets(event);
+ if (String::index(event_line, "delete") >= 0)
+ exit(0);
+ }
+}
+
+#for (int y = 0; y < 20; y++)
+
+void
+show(cairo_t cr, test t)
+{
+ rectangle(cr, 0, 0, 40, 40);
+ set_source_rgba(cr, 1, 1, 1, 1);
+ fill(cr);
+
+ set_source_rgba(cr, 0, 1, 0, .2);
+ set_line_width(cr, 0.1);
+ for (int x = 0; x < 40; x++) {
+ move_to(cr, 0, x);
+ line_to(cr, 40, x);
+ move_to(cr, x, 0);
+ line_to(cr, x, 40);
+ }
+ stroke(cr);
+
+ rectangle(cr, t.b.x1, t.b.y1, t.b.x2 - t.b.x1, t.b.y2 - t.b.y1);
+ set_line_width(cr, 0.1);
+ set_source_rgba(cr, 0, 0, 0, 1);
+ stroke(cr);
+
+ move_to(cr, t.x1+.5, t.y1+.5);
+ line_to(cr, t.x2+.5, t.y2+.5);
+ move_to(cr, t.x2, t.y2);
+ line_to(cr, t.x2+1, t.y2+1);
+ move_to(cr, t.x2+1, t.y2);
+ line_to(cr, t.x2, t.y2+1);
+ stroke(cr);
+
+ void pixels(point[] pt) {
+ for (int i = 0; i < dim(pt); i++) {
+ rectangle(cr, pt[i].x, pt[i].y, 1, 1);
+ }
+ fill(cr);
+ }
+
+ set_source_rgba(cr, 1, 0, 0, .5);
+ pixels(t.clipped);
+
+ set_source_rgba(cr, 0, 0, 1, .5);
+ pixels(t.run);
+}
+
+bool
+compare(test t)
+{
+ if (dim(t.clipped) != dim(t.run))
+ return false;
+
+ for (int i = 0; i < dim(t.clipped); i++)
+ if (t.clipped[i] != t.run[i])
+ return false;
+ return true;
+}
+
+void
+doit(int i)
+{
+ int n;
+ *box b = &bounds;
+
+ cairo_t cr = new(800, 800);
+
+ scale(cr, 20, 20);
+
+ for (;;) {
+ PRNG::srandom(i);
+ int x1 = PRNG::randint(40);
+ int x2 = PRNG::randint(40);
+ int y1 = PRNG::randint(40);
+ int y2 = PRNG::randint(40);
+
+ test t = line (x1, y1, x2, y2, &bounds);
+ show(cr, t);
+ if (!compare(t)) {
+ printf("line %d -- %d x %d - %d x %d\n", i, x1, y1, x2, y2);
+ gets();
+ }
+ i++;
+ }
+
+ read_events(cr);
+}
+
+int i = 0;
+if (dim(argv) > 1)
+ i = atoi(argv[1]);
+
+doit(i);
diff --git a/src/drivers/ao_as1107.c b/src/drivers/ao_as1107.c
new file mode 100644
index 00000000..e0172d95
--- /dev/null
+++ b/src/drivers/ao_as1107.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_as1107.h>
+
+static uint8_t as1107_configured;
+static uint8_t as1107_mutex;
+
+static void
+ao_as1107_start(void) {
+ ao_spi_get_bit(AO_AS1107_CS_PORT, AO_AS1107_CS_PIN, AO_AS1107_CS, AO_AS1107_SPI_INDEX, AO_AS1107_SPI_SPEED);
+}
+
+static void
+ao_as1107_stop(void) {
+ ao_spi_put_bit(AO_AS1107_CS_PORT, AO_AS1107_CS_PIN, AO_AS1107_CS, AO_AS1107_SPI_INDEX);
+}
+
+static void
+_ao_as1107_cmd(uint8_t addr, uint8_t value)
+{
+ uint8_t packet[2] = { addr, value };
+
+ ao_as1107_start();
+ ao_spi_send(packet, 2, AO_AS1107_SPI_INDEX);
+ ao_as1107_stop();
+}
+
+static void
+_ao_as1107_setup(void)
+{
+ if (!as1107_configured) {
+ as1107_configured = 1;
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_SHUTDOWN_RESET);
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_SHUTDOWN_NOP);
+ _ao_as1107_cmd(AO_AS1107_DECODE_MODE, AO_AS1107_DECODE);
+ _ao_as1107_cmd(AO_AS1107_SCAN_LIMIT, AO_AS1107_NUM_DIGITS - 1);
+ _ao_as1107_cmd(AO_AS1107_INTENSITY, 0x0f);
+ _ao_as1107_cmd(AO_AS1107_FEATURE,
+ (0 << AO_AS1107_FEATURE_CLK_EN) |
+ (0 << AO_AS1107_FEATURE_REG_RES) |
+ (1 << AO_AS1107_FEATURE_DECODE_SEL) |
+ (1 << AO_AS1107_FEATURE_SPI_EN) |
+ (0 << AO_AS1107_FEATURE_BLINK_EN) |
+ (0 << AO_AS1107_FEATURE_BLINK_FREQ) |
+ (0 << AO_AS1107_FEATURE_SYNC) |
+ (0 << AO_AS1107_FEATURE_BLINK_START));
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_NORMAL_NOP);
+ }
+}
+
+void
+ao_as1107_write(uint8_t start, uint8_t count, uint8_t *values)
+{
+ uint8_t i;
+ ao_mutex_get(&as1107_mutex);
+ _ao_as1107_setup();
+ for (i = 0; i < count; i++)
+ {
+ _ao_as1107_cmd(AO_AS1107_DIGIT(start + i),
+ values[i]);
+ }
+ ao_mutex_put(&as1107_mutex);
+}
+
+void
+ao_as1107_write_8(uint8_t start, uint8_t value)
+{
+ uint8_t values[2];
+
+ values[0] = (value >> 4);
+ values[1] = value & 0xf;
+ ao_as1107_write(start, 2, values);
+}
+
+void
+ao_as1107_write_16(uint8_t start, uint16_t value)
+{
+ uint8_t values[4];
+
+ values[0] = (value >> 12);
+ values[1] = (value >> 8) & 0xf;
+ values[2] = (value >> 4) & 0xf;
+ values[3] = (value) & 0xf;
+ ao_as1107_write(start, 4, values);
+}
+
+void
+ao_as1107_init(void)
+{
+ as1107_configured = 0;
+ ao_spi_init_cs(AO_AS1107_CS_PORT, (1 << AO_AS1107_CS_PIN));
+}
diff --git a/src/drivers/ao_as1107.h b/src/drivers/ao_as1107.h
new file mode 100644
index 00000000..a22b17db
--- /dev/null
+++ b/src/drivers/ao_as1107.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_AS1107_H_
+#define _AO_AS1107_H_
+
+#define AO_AS1107_NO_OP 0x00
+#define AO_AS1107_DIGIT(n) (0x01 + (n))
+#define AO_AS1107_DECODE_MODE 0x09
+#define AO_AS1107_INTENSITY 0x0a
+#define AO_AS1107_SCAN_LIMIT 0x0b
+#define AO_AS1107_SHUTDOWN 0x0c
+#define AO_AS1107_SHUTDOWN_SHUTDOWN_RESET 0x00
+#define AO_AS1107_SHUTDOWN_SHUTDOWN_NOP 0x80
+#define AO_AS1107_SHUTDOWN_NORMAL_RESET 0x01
+#define AO_AS1107_SHUTDOWN_NORMAL_NOP 0x81
+
+#define AO_AS1107_FEATURE 0x0e
+#define AO_AS1107_FEATURE_CLK_EN 0 /* external clock enable */
+#define AO_AS1107_FEATURE_REG_RES 1
+#define AO_AS1107_FEATURE_DECODE_SEL 2 /* select HEX decode */
+#define AO_AS1107_FEATURE_SPI_EN 3
+#define AO_AS1107_FEATURE_BLINK_EN 4
+#define AO_AS1107_FEATURE_BLINK_FREQ 5
+#define AO_AS1107_FEATURE_SYNC 6
+#define AO_AS1107_FEATURE_BLINK_START 7
+#define AO_AS1107_DISPLAY_TEST 0x0f
+
+void ao_as1107_init(void);
+
+void
+ao_as1107_write(uint8_t start, uint8_t count, uint8_t *values);
+
+void
+ao_as1107_write_8(uint8_t start, uint8_t value);
+
+void
+ao_as1107_write_16(uint8_t start, uint16_t value);
+
+#ifndef AO_AS1107_DECODE
+#error "must define AO_AS1107_DECODE"
+#endif
+
+#ifndef AO_AS1107_NUM_DIGITS
+#error "must define AO_AS1107_NUM_DIGITS"
+#endif
+
+#endif /* _AO_AS1107_H_ */
diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c
index 725ac45a..07e92c67 100644
--- a/src/drivers/ao_button.c
+++ b/src/drivers/ao_button.c
@@ -39,8 +39,16 @@ static struct ao_button_state ao_button_state[AO_BUTTON_COUNT];
#define bit(q) AO_BUTTON_ ## q
#define pin(q) AO_BUTTON_ ## q ## _PIN
+#ifndef AO_BUTTON_INVERTED
+#define AO_BUTTON_INVERTED 1
+#endif
+
+#if AO_BUTTON_INVERTED
/* pins are inverted */
#define ao_button_value(b) !ao_gpio_get(port(b), bit(b), pin(b))
+#else
+#define ao_button_value(b) ao_gpio_get(port(b), bit(b), pin(b))
+#endif
static uint8_t
_ao_button_get(uint8_t b)
diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c
index a67071d2..c1c21e0d 100644
--- a/src/drivers/ao_cc115l.c
+++ b/src/drivers/ao_cc115l.c
@@ -39,7 +39,7 @@ static uint8_t ao_radio_abort; /* radio operation should abort */
#define FOSC 26000000
-#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_6MHz)
+#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_CC115L_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c
index 2bc99734..de282000 100644
--- a/src/drivers/ao_cc1200.c
+++ b/src/drivers/ao_cc1200.c
@@ -51,7 +51,11 @@ extern const uint32_t ao_radio_cal;
#define FOSC 40000000
#endif
-#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST)
+#ifndef AO_CC1200_SPI_SPEED
+#error AO_CC1200_SPI_SPEED undefined
+#endif
+
+#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_CC1200_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC1200_SPI_BUS)
#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1200_SPI_BUS)
@@ -1323,7 +1327,7 @@ static void ao_radio_packet(void) {
void
ao_radio_test_recv(void)
{
- uint8_t bytes[34];
+ static uint8_t bytes[34];
uint8_t b;
if (ao_radio_recv(bytes, 34, 0)) {
diff --git a/src/drivers/ao_console.c b/src/drivers/ao_console.c
new file mode 100644
index 00000000..cbde38c9
--- /dev/null
+++ b/src/drivers/ao_console.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_console.h"
+#include "ao_ps2.h"
+#include "ao_vga.h"
+
+static uint8_t console_row, console_col;
+
+#define ao_console_bitmap ao_vga_bitmap
+
+static uint8_t console_rows, console_cols;
+
+static void
+ao_console_scroll(void)
+{
+ ao_copy(&ao_console_bitmap,
+ 0, 0,
+ ao_console_bitmap.width,
+ ao_console_bitmap.height - ao_font.height,
+ &ao_console_bitmap,
+ 0, ao_font.height,
+ AO_COPY);
+ ao_rect(&ao_console_bitmap,
+ 0,
+ (console_rows - 1) * ao_font.height,
+ ao_console_bitmap.width,
+ ao_font.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_cursor(void)
+{
+ ao_rect(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height,
+ ao_font.width,
+ ao_font.height,
+ 1,
+ AO_XOR);
+}
+
+static void
+ao_console_clear(void)
+{
+ ao_rect(&ao_console_bitmap,
+ 0, 0,
+ ao_console_bitmap.width,
+ ao_console_bitmap.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_space(void)
+{
+ ao_rect(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height,
+ ao_font.width,
+ ao_font.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_newline(void)
+{
+ if (++console_row == console_rows) {
+ ao_console_scroll();
+ console_row--;
+ }
+}
+
+void
+ao_console_putchar(char c)
+{
+ if (' ' <= c && c < 0x7f) {
+ char text[2];
+ ao_console_space();
+ text[0] = c;
+ text[1] = '\0';
+ ao_text(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height + ao_font.ascent,
+ text,
+ 0,
+ AO_COPY);
+ if (++console_col == console_cols) {
+ console_col = 0;
+ ao_console_newline();
+ }
+ } else {
+ ao_console_cursor();
+ switch (c) {
+ case '\r':
+ console_col = 0;
+ break;
+ case '\t':
+ console_col += 8 - (console_col & 7);
+ if (console_col >= console_cols) {
+ console_col = 0;
+ ao_console_newline();
+ }
+ break;
+ case '\n':
+ ao_console_newline();
+ break;
+ case '\f':
+ console_col = console_row = 0;
+ ao_console_clear();
+ break;
+ case '\177':
+ case '\010':
+ if (console_col)
+ console_col--;
+ break;
+ }
+ }
+ ao_console_cursor();
+}
+
+void
+ao_console_init(void)
+{
+ console_cols = ao_console_bitmap.width / ao_font.width;
+ console_rows = ao_console_bitmap.height / ao_font.height;
+#if CONSOLE_STDIN
+ ao_ps2_stdin = 1;
+ ao_add_stdio(_ao_ps2_pollchar,
+ ao_console_putchar,
+ NULL);
+#endif
+ ao_console_clear();
+ ao_console_cursor();
+ ao_vga_enable(1);
+}
diff --git a/src/drivers/ao_console.h b/src/drivers/ao_console.h
new file mode 100644
index 00000000..33d3658a
--- /dev/null
+++ b/src/drivers/ao_console.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_CONSOLE_H_
+#define _AO_CONSOLE_H_
+
+void
+ao_console_putchar(char c);
+
+void
+ao_console_init(void);
+
+#endif /* _AO_CONSOLE_H_ */
diff --git a/src/drivers/ao_event.h b/src/drivers/ao_event.h
index d1c69d81..d1df6eac 100644
--- a/src/drivers/ao_event.h
+++ b/src/drivers/ao_event.h
@@ -22,6 +22,7 @@
#define AO_EVENT_NONE 0
#define AO_EVENT_QUADRATURE 1
#define AO_EVENT_BUTTON 2
+#define AO_EVENT_KEY 3
struct ao_event {
uint8_t type;
diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c
index fb8eecff..43e7df23 100644
--- a/src/drivers/ao_fat.c
+++ b/src/drivers/ao_fat.c
@@ -1615,7 +1615,7 @@ ao_fat_hexdump_cmd(void)
ao_cmd_status = ao_cmd_syntax_error;
return;
}
-
+
fd = ao_fat_open(name, AO_FAT_OPEN_READ);
if (fd < 0) {
printf ("Open failed: %d\n", fd);
@@ -1649,5 +1649,7 @@ void
ao_fat_init(void)
{
ao_bufio_init();
+#if FAT_COMMANDS
ao_cmd_register(&ao_fat_cmds[0]);
+#endif
}
diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c
index 00f10ecc..e1806ca3 100644
--- a/src/drivers/ao_lco.c
+++ b/src/drivers/ao_lco.c
@@ -661,7 +661,7 @@ ao_lco_monitor(void)
ao_lco_armed, ao_lco_firing);
if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite();
+ ao_lco_ignite(AO_PAD_FIRE);
} else {
ao_lco_update();
if (ao_lco_armed) {
diff --git a/src/drivers/ao_lco_cmd.c b/src/drivers/ao_lco_cmd.c
index dcc0c6d0..8de21fb6 100644
--- a/src/drivers/ao_lco_cmd.c
+++ b/src/drivers/ao_lco_cmd.c
@@ -61,9 +61,9 @@ lco_arm(void)
}
static void
-lco_ignite(void)
+lco_ignite(uint8_t cmd)
{
- ao_lco_ignite();
+ ao_lco_ignite(cmd);
}
static void
@@ -145,7 +145,40 @@ lco_fire_cmd(void) __reentrant
secs = 100;
for (i = 0; i < secs; i++) {
printf("fire %d\n", i); flush();
- lco_ignite();
+ lco_ignite(AO_PAD_FIRE);
+ ao_delay(AO_MS_TO_TICKS(100));
+ }
+}
+
+static void
+lco_static_cmd(void) __reentrant
+{
+ uint8_t secs;
+ uint8_t i;
+ int8_t r;
+
+ lco_args();
+ ao_cmd_decimal();
+ secs = ao_cmd_lex_i;
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ r = lco_query();
+ if (r != AO_RADIO_CMAC_OK) {
+ printf("query failed %d\n", r);
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ printf("arm %d\n", i); flush();
+ lco_arm();
+ }
+
+ secs = secs * 10 - 5;
+ if (secs > 100)
+ secs = 100;
+ for (i = 0; i < secs; i++) {
+ printf("fire %d\n", i); flush();
+ lco_ignite(AO_PAD_STATIC);
ao_delay(AO_MS_TO_TICKS(100));
}
}
@@ -171,12 +204,22 @@ lco_ignite_cmd(void) __reentrant
uint8_t i;
lco_args();
for (i = 0; i < 4; i++)
- lco_ignite();
+ lco_ignite(AO_PAD_FIRE);
+}
+
+
+static void
+lco_endstatic_cmd(void) __reentrant
+{
+ lco_ignite(AO_PAD_ENDSTATIC);
}
static __code struct ao_cmds ao_lco_cmds[] = {
{ lco_report_cmd, "l <box> <channel>\0Get remote status" },
{ lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
+ { lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
+ { lco_static_cmd, "S <box> <channel> <secs>\0Initiate static test" },
+ { lco_endstatic_cmd, "D\0End static test (and download someday)" },
{ lco_arm_cmd, "a <box> <channel>\0Arm remote igniter" },
{ lco_ignite_cmd, "i <box> <channel>\0Pulse remote igniter" },
{ 0, NULL },
diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c
index 862cb1be..92b344ed 100644
--- a/src/drivers/ao_lco_func.c
+++ b/src/drivers/ao_lco_func.c
@@ -47,7 +47,7 @@ ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset)
ao_mutex_get(&ao_lco_mutex);
command.tick = ao_time();
command.box = box;
- command.cmd = AO_LAUNCH_QUERY;
+ command.cmd = AO_PAD_QUERY;
command.channels = 0;
ao_radio_cmac_send(&command, sizeof (command));
sent_time = ao_time();
@@ -64,19 +64,19 @@ ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset)
ao_mutex_get(&ao_lco_mutex);
command.tick = ao_time() - tick_offset;
command.box = box;
- command.cmd = AO_LAUNCH_ARM;
+ command.cmd = AO_PAD_ARM;
command.channels = channels;
ao_radio_cmac_send(&command, sizeof (command));
ao_mutex_put(&ao_lco_mutex);
}
void
-ao_lco_ignite(void)
+ao_lco_ignite(uint8_t cmd)
{
ao_mutex_get(&ao_lco_mutex);
command.tick = 0;
command.box = 0;
- command.cmd = AO_LAUNCH_FIRE;
+ command.cmd = cmd;
command.channels = 0;
ao_radio_cmac_send(&command, sizeof (command));
ao_mutex_put(&ao_lco_mutex);
diff --git a/src/drivers/ao_lco_func.h b/src/drivers/ao_lco_func.h
index 6b06f928..9d4a27ba 100644
--- a/src/drivers/ao_lco_func.h
+++ b/src/drivers/ao_lco_func.h
@@ -28,6 +28,6 @@ void
ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset);
void
-ao_lco_ignite(void);
+ao_lco_ignite(uint8_t cmd);
#endif /* _AO_LCO_FUNC_H_ */
diff --git a/src/drivers/ao_lco_two.c b/src/drivers/ao_lco_two.c
index 1cb0546c..e2f86745 100644
--- a/src/drivers/ao_lco_two.c
+++ b/src/drivers/ao_lco_two.c
@@ -287,7 +287,7 @@ ao_lco_monitor(void)
ao_lco_armed, ao_lco_firing);
if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite();
+ ao_lco_ignite(AO_PAD_FIRE);
} else {
ao_lco_get_channels();
if (ao_lco_armed) {
diff --git a/src/drivers/ao_matrix.c b/src/drivers/ao_matrix.c
new file mode 100644
index 00000000..fa2d0c57
--- /dev/null
+++ b/src/drivers/ao_matrix.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_matrix.h>
+#include <ao_event.h>
+#include <ao_exti.h>
+
+#define row_port(q) AO_MATRIX_ROW_ ## q ## _PORT
+#define row_bit(q) AO_MATRIX_ROW_ ## q ## _PIN
+#define row_pin(q) AO_MATRIX_ROW_ ## q ## _PIN
+
+#define col_port(q) AO_MATRIX_COL_ ## q ## _PORT
+#define col_bit(q) AO_MATRIX_COL_ ## q ## _PIN
+#define col_pin(q) AO_MATRIX_COL_ ## q ## _PIN
+
+static void
+_ao_matrix_drive_row(uint8_t row, uint8_t val)
+{
+ switch (row) {
+#define drive(n) case n: ao_gpio_set(row_port(n), row_bit(n), row_pin(n), val); break
+ drive(0);
+#if AO_MATRIX_ROWS > 1
+ drive(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ drive(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ drive(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ drive(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ drive(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ drive(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ drive(7);
+#endif
+ }
+}
+
+static uint8_t
+_ao_matrix_read_cols(void)
+{
+ uint8_t v = 0;
+#define read(n) (v |= ao_gpio_get(col_port(n), col_bit(n), col_pin(n)) << n)
+
+ read(0);
+#if AO_MATRIX_ROWS > 1
+ read(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ read(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ read(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ read(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ read(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ read(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ read(7);
+#endif
+ return v;
+}
+
+static uint8_t
+_ao_matrix_read(uint8_t row) {
+ uint8_t state;
+ _ao_matrix_drive_row(row, 0);
+ state = _ao_matrix_read_cols();
+ _ao_matrix_drive_row(row, 1);
+ return state;
+}
+
+#define AO_MATRIX_DEBOUNCE_INTERVAL AO_MS_TO_TICKS(50)
+
+static uint8_t ao_matrix_keymap[AO_MATRIX_ROWS][AO_MATRIX_COLS] = AO_MATRIX_KEYCODES;
+
+static uint8_t ao_matrix_state[AO_MATRIX_ROWS];
+static AO_TICK_TYPE ao_matrix_tick[AO_MATRIX_ROWS];
+
+static void
+_ao_matrix_poll_one(uint8_t row) {
+ uint8_t state = _ao_matrix_read(row);
+
+ if (state != ao_matrix_state[row]) {
+ AO_TICK_TYPE now = ao_time();
+
+ if ((now - ao_matrix_tick[row]) >= AO_MATRIX_DEBOUNCE_INTERVAL) {
+ uint8_t col;
+ uint8_t changes = state ^ ao_matrix_state[row];
+
+ for (col = 0; col < AO_MATRIX_COLS; col++) {
+ if (changes & (1 << col)) {
+ ao_event_put_isr(AO_EVENT_KEY,
+ ao_matrix_keymap[row][col],
+ ((state >> col) & 1) == 0);
+ }
+ }
+ ao_matrix_state[row] = state;
+ }
+ ao_matrix_tick[row] = now;
+ }
+}
+
+void
+ao_matrix_poll(void)
+{
+ uint8_t row;
+
+ for (row = 0; row < AO_MATRIX_ROWS; row++)
+ _ao_matrix_poll_one(row);
+}
+
+#define init_row(b) do { \
+ ao_enable_output(row_port(b), row_bit(b), row_pin(v), 1); \
+ ao_gpio_set_output_mode(row_port(b), row_bit(b), row_pin(b), AO_OUTPUT_OPEN_DRAIN); \
+ } while (0)
+
+#define init_col(b) do { \
+ ao_enable_input(col_port(b), col_bit(b), AO_EXTI_MODE_PULL_UP); \
+ } while(0)
+
+void
+ao_matrix_init(void)
+{
+ uint8_t row;
+
+ init_row(0);
+#if AO_MATRIX_ROWS > 1
+ init_row(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ init_row(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ init_row(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ init_row(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ init_row(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ init_row(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ init_row(7);
+#endif
+
+ init_col(0);
+#if AO_MATRIX_COLS > 1
+ init_col(1);
+#endif
+#if AO_MATRIX_COLS > 2
+ init_col(2);
+#endif
+#if AO_MATRIX_COLS > 3
+ init_col(3);
+#endif
+#if AO_MATRIX_COLS > 4
+ init_col(4);
+#endif
+#if AO_MATRIX_COLS > 5
+ init_col(5);
+#endif
+#if AO_MATRIX_COLS > 6
+ init_col(6);
+#endif
+#if AO_MATRIX_COLS > 7
+ init_col(7);
+#endif
+ for (row = 0; row < AO_MATRIX_ROWS; row++) {
+ ao_matrix_state[row] = _ao_matrix_read(row);
+ ao_matrix_tick[row] = ao_time();
+ }
+}
diff --git a/src/drivers/ao_matrix.h b/src/drivers/ao_matrix.h
new file mode 100644
index 00000000..ab5a1c51
--- /dev/null
+++ b/src/drivers/ao_matrix.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_MATRIX_H_
+#define _AO_MATRIX_H_
+
+void
+ao_matrix_poll(void);
+
+void
+ao_matrix_init(void);
+
+#endif /* _AO_MATRIX_H_ */
diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c
index ffa833fe..16b4ae60 100644
--- a/src/drivers/ao_pad.c
+++ b/src/drivers/ao_pad.c
@@ -316,7 +316,7 @@ ao_pad(void)
command.tick, command.box, ao_pad_box, command.cmd, command.channels);
switch (command.cmd) {
- case AO_LAUNCH_ARM:
+ case AO_PAD_ARM:
if (command.box != ao_pad_box) {
PRINTD ("box number mismatch\n");
break;
@@ -338,7 +338,7 @@ ao_pad(void)
ao_pad_arm_time = ao_time();
break;
- case AO_LAUNCH_QUERY:
+ case AO_PAD_QUERY:
if (command.box != ao_pad_box) {
PRINTD ("box number mismatch\n");
break;
@@ -357,7 +357,7 @@ ao_pad(void)
query.igniter_status[3]);
ao_radio_cmac_send(&query, sizeof (query));
break;
- case AO_LAUNCH_FIRE:
+ case AO_PAD_FIRE:
if (!ao_pad_armed) {
PRINTD ("not armed\n");
break;
@@ -372,6 +372,29 @@ ao_pad(void)
ao_pad_arm_time = ao_time();
ao_wakeup(&ao_pad_ignite);
break;
+ case AO_PAD_STATIC:
+ if (!ao_pad_armed) {
+ PRINTD ("not armed\n");
+ break;
+ }
+#if HAS_LOG
+ if (!ao_log_running) ao_log_start();
+#endif
+ if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
+ PRINTD ("late pad arm_time %d time %d\n",
+ ao_pad_arm_time, ao_time());
+ break;
+ }
+ PRINTD ("ignite\n");
+ ao_pad_ignite = ao_pad_armed;
+ ao_pad_arm_time = ao_time();
+ ao_wakeup(&ao_pad_ignite);
+ break;
+ case AO_PAD_ENDSTATIC:
+#if HAS_LOG
+ ao_log_stop();
+#endif
+ break;
}
}
}
diff --git a/src/drivers/ao_pad.h b/src/drivers/ao_pad.h
index 648d3005..e4115222 100644
--- a/src/drivers/ao_pad.h
+++ b/src/drivers/ao_pad.h
@@ -54,6 +54,11 @@ struct ao_pad_query {
*/
#define AO_PAD_FIRE 3
+/* Fire current armed pads for 200ms, no report, logging test stand sensors
+ */
+#define AO_PAD_STATIC 4
+#define AO_PAD_ENDSTATIC 5
+
#define AO_PAD_FIRE_TIME AO_MS_TO_TICKS(200)
#define AO_PAD_ARM_STATUS_DISARMED 0
diff --git a/src/drivers/ao_ps2.c b/src/drivers/ao_ps2.c
new file mode 100644
index 00000000..29eecea8
--- /dev/null
+++ b/src/drivers/ao_ps2.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_ps2.h"
+#include "ao_exti.h"
+
+static struct ao_fifo ao_ps2_rx_fifo;
+
+static uint16_t ao_ps2_tx;
+static uint8_t ao_ps2_tx_count;
+
+static AO_TICK_TYPE ao_ps2_tick;
+static uint16_t ao_ps2_value;
+static uint8_t ao_ps2_count;
+
+uint8_t ao_ps2_stdin;
+
+uint8_t ao_ps2_scancode_set;
+
+#define AO_PS2_CLOCK_MODE(pull) ((pull) | AO_EXTI_MODE_FALLING | AO_EXTI_PRIORITY_MED)
+
+static void
+ao_ps2_isr(void);
+
+static uint8_t
+_ao_ps2_parity(uint8_t value)
+{
+ uint8_t parity = 1;
+ uint8_t b;
+
+ for (b = 0; b < 8; b++) {
+ parity ^= (value & 1);
+ value >>= 1;
+ }
+ return parity;
+}
+
+static int
+_ao_ps2_poll(void)
+{
+ uint8_t u;
+ if (ao_fifo_empty(ao_ps2_rx_fifo)) {
+ return AO_READ_AGAIN;
+ }
+ ao_fifo_remove(ao_ps2_rx_fifo, u);
+
+ return (int) u;
+}
+
+uint8_t
+ao_ps2_get(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_poll()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+
+int
+ao_ps2_poll(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ c = _ao_ps2_poll();
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+void
+ao_ps2_put(uint8_t c)
+{
+ ao_arch_block_interrupts();
+ ao_ps2_tx = ((uint16_t) c) | (_ao_ps2_parity(c) << 8) | (3 << 9);
+ ao_ps2_tx_count = 11;
+ ao_exti_disable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+ ao_arch_release_interrupts();
+
+ /* pull the clock pin down */
+ ao_enable_output(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT, AO_PS2_CLOCK_PIN, 0);
+ ao_delay(0);
+
+ /* pull the data pin down for the start bit */
+ ao_enable_output(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, 0);
+ ao_delay(0);
+
+ /* switch back to input mode for the interrupt to work */
+ ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+ AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+ ao_ps2_isr);
+ ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+ /* wait for the bits to drain */
+ while (ao_ps2_tx_count)
+ ao_sleep(&ao_ps2_tx_count);
+
+}
+
+static uint8_t ao_ps2_down[128 / 8];
+
+static void
+ao_ps2_set_down(uint8_t code, uint8_t value)
+{
+ uint8_t shift = (code & 0x07);
+ uint8_t byte = code >> 3;
+
+ ao_ps2_down[byte] = (ao_ps2_down[byte] & ~(1 << shift)) | (value << shift);
+}
+
+uint8_t
+ao_ps2_is_down(uint8_t code)
+{
+ uint8_t shift = (code & 0x07);
+ uint8_t byte = code >> 3;
+
+ return (ao_ps2_down[byte] >> shift) & 1;
+}
+
+static void
+_ao_ps2_set_leds(void)
+{
+ uint8_t led = 0;
+ if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+ led |= AO_PS2_SET_LEDS_CAPS;
+ if (ao_ps2_is_down(AO_PS2_NUM_LOCK))
+ led |= AO_PS2_SET_LEDS_NUM;
+ if (ao_ps2_is_down(AO_PS2_SCROLL_LOCK))
+ led |= AO_PS2_SET_LEDS_SCROLL;
+ ao_arch_release_interrupts();
+ ao_ps2_put(AO_PS2_SET_LEDS);
+ while (ao_ps2_get() != 0xfa);
+ ao_ps2_put(led);
+ ao_arch_block_interrupts();
+}
+
+static uint8_t
+ao_ps2_is_lock(uint8_t code) {
+ switch (code) {
+ case AO_PS2_CAPS_LOCK:
+ case AO_PS2_NUM_LOCK:
+ case AO_PS2_SCROLL_LOCK:
+ return 1;
+ }
+ return 0;
+}
+
+static void
+_ao_ps2_set_scancode_set(uint8_t set)
+{
+ ao_ps2_scancode_set = set;
+ ao_arch_release_interrupts();
+ ao_ps2_put(AO_PS2_SET_SCAN_CODE_SET);
+ while (ao_ps2_get() != 0xfa);
+ ao_ps2_put(set);
+ ao_ps2_put(AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK);
+ while (ao_ps2_get() != 0xfa);
+ ao_arch_block_interrupts();
+}
+
+static int
+_ao_ps2_poll_key(void)
+{
+ int c;
+ uint8_t set_led = 0;
+ static uint8_t saw_break;
+
+ c = _ao_ps2_poll();
+ if (c < 0) {
+ if (ao_ps2_scancode_set != 3) {
+ _ao_ps2_set_scancode_set(3);
+ }
+ return c;
+ }
+
+ if (c == AO_PS2_BREAK) {
+ saw_break = 1;
+ return AO_READ_AGAIN;
+ }
+ if (c & 0x80)
+ return AO_READ_AGAIN;
+
+ if (ao_ps2_is_lock(c)) {
+ if (saw_break) {
+ saw_break = 0;
+ return AO_READ_AGAIN;
+ }
+ if (ao_ps2_is_down(c))
+ saw_break = 1;
+ set_led = 1;
+ }
+ if (saw_break) {
+ saw_break = 0;
+ ao_ps2_set_down(c, 0);
+ c |= 0x80;
+ } else
+ ao_ps2_set_down(c, 1);
+ if (set_led)
+ _ao_ps2_set_leds();
+
+ if (ao_ps2_scancode_set != 3)
+ _ao_ps2_set_scancode_set(3);
+
+ return c;
+}
+
+int
+ao_ps2_poll_key(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ c = _ao_ps2_poll_key();
+ ao_arch_release_interrupts();
+ return c;
+}
+
+uint8_t
+ao_ps2_get_key(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_poll_key()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+static const uint8_t ao_ps2_asciimap[128][2] = {
+ [AO_PS2_A] = { 'a', 'A' },
+ [AO_PS2_B] = { 'b', 'B' },
+ [AO_PS2_C] = { 'c', 'C' },
+ [AO_PS2_D] = { 'd', 'D' },
+ [AO_PS2_E] = { 'e', 'E' },
+ [AO_PS2_F] = { 'f', 'F' },
+ [AO_PS2_G] = { 'g', 'G' },
+ [AO_PS2_H] = { 'h', 'H' },
+ [AO_PS2_I] = { 'i', 'I' },
+ [AO_PS2_J] = { 'j', 'J' },
+ [AO_PS2_K] = { 'k', 'K' },
+ [AO_PS2_L] = { 'l', 'L' },
+ [AO_PS2_M] = { 'm', 'M' },
+ [AO_PS2_N] = { 'n', 'N' },
+ [AO_PS2_O] = { 'o', 'O' },
+ [AO_PS2_P] = { 'p', 'P' },
+ [AO_PS2_Q] = { 'q', 'Q' },
+ [AO_PS2_R] = { 'r', 'R' },
+ [AO_PS2_S] = { 's', 'S' },
+ [AO_PS2_T] = { 't', 'T' },
+ [AO_PS2_U] = { 'u', 'U' },
+ [AO_PS2_V] = { 'v', 'V' },
+ [AO_PS2_W] = { 'w', 'W' },
+ [AO_PS2_X] = { 'x', 'X' },
+ [AO_PS2_Y] = { 'y', 'Y' },
+ [AO_PS2_Z] = { 'z', 'Z' },
+
+ [AO_PS2_0] = { '0', ')' },
+ [AO_PS2_1] = { '1', '!' },
+ [AO_PS2_2] = { '2', '@' },
+ [AO_PS2_3] = { '3', '#' },
+ [AO_PS2_4] = { '4', '$' },
+ [AO_PS2_5] = { '5', '%' },
+ [AO_PS2_6] = { '6', '^' },
+ [AO_PS2_7] = { '7', '&' },
+ [AO_PS2_8] = { '8', '*' },
+ [AO_PS2_9] = { '9', '(' },
+
+ [AO_PS2_GRAVE] = { '`', '~' },
+ [AO_PS2_HYPHEN] = { '-', '_' },
+ [AO_PS2_EQUAL] = { '=', '+' },
+ [AO_PS2_BACKSLASH] = { '\\', '|' },
+ [AO_PS2_BACKSPACE] = { '\010', '\010' },
+ [AO_PS2_SPACE] = { ' ', ' ' },
+ [AO_PS2_TAB] = { '\t', '\t' },
+
+ [AO_PS2_ENTER] = { '\r', '\r' },
+ [AO_PS2_ESC] = { '\033', '\033' },
+
+ [AO_PS2_OPEN_SQ] = { '[', '{' },
+ [AO_PS2_DELETE] = { '\177', '\177' },
+
+ [AO_PS2_KP_TIMES] = { '*', '*' },
+ [AO_PS2_KP_PLUS] = { '+', '+' },
+ [AO_PS2_KP_ENTER] = { '\r', '\r' },
+ [AO_PS2_KP_DECIMAL] = { '.', '.' },
+ [AO_PS2_KP_0] = { '0', '0' },
+ [AO_PS2_KP_1] = { '1', '1' },
+ [AO_PS2_KP_2] = { '2', '2' },
+ [AO_PS2_KP_3] = { '3', '3' },
+ [AO_PS2_KP_4] = { '4', '4' },
+ [AO_PS2_KP_5] = { '5', '5' },
+ [AO_PS2_KP_6] = { '6', '6' },
+ [AO_PS2_KP_7] = { '7', '7' },
+ [AO_PS2_KP_8] = { '8', '8' },
+ [AO_PS2_KP_9] = { '9', '9' },
+ [AO_PS2_CLOSE_SQ] = { ']', '}' },
+ [AO_PS2_SEMICOLON] = { ';', ':' },
+ [AO_PS2_ACUTE] = { '\'', '"' },
+ [AO_PS2_COMMA] = { ',', '<' },
+ [AO_PS2_PERIOD] = { '.', '>' },
+ [AO_PS2_SLASH] = { '/', '?' },
+};
+
+int
+ao_ps2_ascii(uint8_t key)
+{
+ uint8_t col;
+ char a;
+
+ /* Skip key releases */
+ if (key & 0x80)
+ return AO_READ_AGAIN;
+
+ col = 0;
+ if (ao_ps2_is_down(AO_PS2_L_SHIFT) || ao_ps2_is_down(AO_PS2_R_SHIFT))
+ col = 1;
+
+ /* caps lock */
+ a = ao_ps2_asciimap[key][0];
+ if (!a)
+ return AO_READ_AGAIN;
+
+ if ('a' <= a && a <= 'z')
+ if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+ col ^= 1;
+ a = ao_ps2_asciimap[key][col];
+ if ('@' <= a && a <= 0x7f && (ao_ps2_is_down(AO_PS2_L_CTRL) || ao_ps2_is_down(AO_PS2_R_CTRL)))
+ a &= 0x1f;
+ return a;
+}
+
+int
+_ao_ps2_pollchar(void)
+{
+ int key;
+
+ key = _ao_ps2_poll_key();
+ if (key < 0)
+ return key;
+ return ao_ps2_ascii(key);
+}
+
+char
+ao_ps2_getchar(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_pollchar()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static void
+ao_ps2_isr(void)
+{
+ uint8_t bit;
+
+ if (ao_ps2_tx_count) {
+ ao_gpio_set(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, ao_ps2_tx&1);
+ ao_ps2_tx >>= 1;
+ ao_ps2_tx_count--;
+ if (!ao_ps2_tx_count) {
+ ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_wakeup(&ao_ps2_tx_count);
+ }
+ return;
+ }
+ /* reset if its been a while */
+ if ((ao_tick_count - ao_ps2_tick) > AO_MS_TO_TICKS(100))
+ ao_ps2_count = 0;
+ ao_ps2_tick = ao_tick_count;
+
+ bit = ao_gpio_get(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN);
+ if (ao_ps2_count == 0) {
+ /* check for start bit, ignore if not zero */
+ if (bit)
+ return;
+ ao_ps2_value = 0;
+ } else if (ao_ps2_count < 9) {
+ ao_ps2_value |= (bit << (ao_ps2_count - 1));
+ } else if (ao_ps2_count == 10) {
+ ao_fifo_insert(ao_ps2_rx_fifo, ao_ps2_value);
+ ao_wakeup(&ao_ps2_rx_fifo);
+ if (ao_ps2_stdin)
+ ao_wakeup(&ao_stdin_ready);
+ ao_ps2_count = 0;
+ return;
+ }
+ ao_ps2_count++;
+}
+
+void
+ao_ps2_init(void)
+{
+ ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT,
+ AO_EXTI_MODE_PULL_UP);
+
+ ao_enable_port(AO_PS2_CLOCK_PORT);
+
+ ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+ AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+ ao_ps2_isr);
+ ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+ ao_ps2_scancode_set = 2;
+}
diff --git a/src/drivers/ao_ps2.h b/src/drivers/ao_ps2.h
new file mode 100644
index 00000000..f1f05ee5
--- /dev/null
+++ b/src/drivers/ao_ps2.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_PS2_H_
+#define _AO_PS2_H_
+
+extern uint8_t ao_ps2_stdin;
+
+int
+ao_ps2_poll(void);
+
+uint8_t
+ao_ps2_get(void);
+
+void
+ao_ps2_put(uint8_t b);
+
+uint8_t
+ao_ps2_is_down(uint8_t code);
+
+int
+ao_ps2_poll_key(void);
+
+uint8_t
+ao_ps2_get_key(void);
+
+int
+ao_ps2_ascii(uint8_t key);
+
+int
+_ao_ps2_pollchar(void);
+
+char
+ao_ps2_getchar(void);
+
+void
+ao_ps2_init(void);
+
+/* From http://computer-engineering.org/ps2keyboard/ */
+
+/* Device responds with ACK and then resets */
+#define AO_PS2_RESET 0xff
+
+/* Device retransmits last byte */
+#define AO_PS2_RESEND 0xfe
+
+/* Setting key report only works in mode 3 */
+
+/* Disable break and typematic for specified mode 3 keys. Terminate with invalid key */
+#define AO_PS2_SET_KEY_MAKE 0xfd
+
+/* Disable typematic for keys */
+#define AO_PS2_SET_KEY_MAKE_BREAK 0xfc
+
+/* Disable break code for keys */
+#define AO_PS2_SET_KEY_TYPEMATIC 0xfb
+
+/* Enable make, break and typematic */
+#define AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK 0xfa
+
+/* Disable break and typematic for all */
+#define AO_PS2_SET_ALL_MAKE 0xf9
+
+/* Disable typematic for all */
+#define AO_PS2_SET_ALL_MAKE_BREAK 0xf8
+
+/* Disable break for all */
+#define AO_PS2_SET_ALL_TYPEMATIC 0xf7
+
+/* Set keyboard to default (repeat, report and scan code set 2) */
+#define AO_PS2_SET_DEFAULT 0xf6
+
+/* Disable and reset to default */
+#define AO_PS2_DISABLE 0xf5
+
+/* Enable */
+#define AO_PS2_ENABLE 0xf4
+
+/* Set repeat rate. Bytes 5-6 are the start delay, bits 0-4 are the rate */
+#define AO_PS2_SET_REPEAT_RATE 0xf3
+
+/* Read keyboard id. Returns two bytes */
+#define AO_PS2_GETID 0xf2
+
+/* Set scan code (1, 2, or 3) */
+#define AO_PS2_SET_SCAN_CODE_SET 0xf0
+
+/* Echo. Keyboard replies with Echo */
+#define AO_PS2_ECHO 0xee
+
+/* Set LEDs */
+#define AO_PS2_SET_LEDS 0xed
+# define AO_PS2_SET_LEDS_SCROLL 0x01
+# define AO_PS2_SET_LEDS_NUM 0x02
+# define AO_PS2_SET_LEDS_CAPS 0x04
+
+#define AO_PS2_BREAK 0xf0
+#define AO_PS2_ACK 0xfa
+#define AO_PS2_ERROR 0xfc
+#define AO_PS2_NAK 0xfe
+
+/* Scan code set 3 */
+
+#define AO_PS2_A 0x1c
+#define AO_PS2_B 0x32
+#define AO_PS2_C 0x21
+#define AO_PS2_D 0x23
+#define AO_PS2_E 0x24
+#define AO_PS2_F 0x2b
+#define AO_PS2_G 0x34
+#define AO_PS2_H 0x33
+#define AO_PS2_I 0x43
+#define AO_PS2_J 0x3b
+#define AO_PS2_K 0x42
+#define AO_PS2_L 0x4b
+#define AO_PS2_M 0x3a
+#define AO_PS2_N 0x31
+#define AO_PS2_O 0x44
+#define AO_PS2_P 0x4d
+#define AO_PS2_Q 0x15
+#define AO_PS2_R 0x2d
+#define AO_PS2_S 0x1b
+#define AO_PS2_T 0x2c
+#define AO_PS2_U 0x3c
+#define AO_PS2_V 0x2a
+#define AO_PS2_W 0x1d
+#define AO_PS2_X 0x22
+#define AO_PS2_Y 0x35
+#define AO_PS2_Z 0x1a
+#define AO_PS2_0 0x45
+#define AO_PS2_1 0x16
+#define AO_PS2_2 0x1e
+#define AO_PS2_3 0x26
+#define AO_PS2_4 0x25
+#define AO_PS2_5 0x2e
+#define AO_PS2_6 0x36
+#define AO_PS2_7 0x3d
+#define AO_PS2_8 0x3e
+#define AO_PS2_9 0x46
+#define AO_PS2_GRAVE 0x0e
+#define AO_PS2_HYPHEN 0x4e
+#define AO_PS2_EQUAL 0x55
+#define AO_PS2_BACKSLASH 0x5c
+#define AO_PS2_BACKSPACE 0x66
+#define AO_PS2_SPACE 0x29
+#define AO_PS2_TAB 0x0d
+#define AO_PS2_CAPS_LOCK 0x14
+#define AO_PS2_L_SHIFT 0x12
+#define AO_PS2_L_CTRL 0x11
+#define AO_PS2_L_WIN 0x8b
+#define AO_PS2_L_ALT 0x19
+#define AO_PS2_R_SHIFT 0x59
+#define AO_PS2_R_CTRL 0x58
+#define AO_PS2_R_WIN 0x8c
+#define AO_PS2_R_ALT 0x39
+#define AO_PS2_APPS 0x8d
+#define AO_PS2_ENTER 0x5a
+#define AO_PS2_ESC 0x08
+#define AO_PS2_F1 0x07
+#define AO_PS2_F2 0x0f
+#define AO_PS2_F3 0x17
+#define AO_PS2_F4 0x1f
+#define AO_PS2_F5 0x27
+#define AO_PS2_F6 0x2f
+#define AO_PS2_F7 0x37
+#define AO_PS2_F8 0x3f
+#define AO_PS2_F9 0x47
+#define AO_PS2_F10 0x4f
+#define AO_PS2_F11 0x56
+#define AO_PS2_F12 0x5e
+#define AO_PS2_PRNT_SCRN 0x57
+#define AO_PS2_SCROLL_LOCK 0x5f
+#define AO_PS2_PAUSE 0x62
+#define AO_PS2_OPEN_SQ 0x54
+#define AO_PS2_INSERT 0x67
+#define AO_PS2_HOME 0x6e
+#define AO_PS2_PG_UP 0x6f
+#define AO_PS2_DELETE 0x64
+#define AO_PS2_END 0x65
+#define AO_PS2_PG_DN 0x6d
+#define AO_PS2_UP 0x63
+#define AO_PS2_LEFT 0x61
+#define AO_PS2_DOWN 0x60
+#define AO_PS2_RIGHT 0x6a
+#define AO_PS2_NUM_LOCK 0x76
+#define AO_PS2_KP_TIMES 0x7e
+#define AO_PS2_KP_PLUS 0x7c
+#define AO_PS2_KP_ENTER 0x79
+#define AO_PS2_KP_DECIMAL 0x71
+#define AO_PS2_KP_0 0x70
+#define AO_PS2_KP_1 0x69
+#define AO_PS2_KP_2 0x72
+#define AO_PS2_KP_3 0x7a
+#define AO_PS2_KP_4 0x6b
+#define AO_PS2_KP_5 0x73
+#define AO_PS2_KP_6 0x74
+#define AO_PS2_KP_7 0x6c
+#define AO_PS2_KP_8 0x75
+#define AO_PS2_KP_9 0x7d
+#define AO_PS2_CLOSE_SQ 0x5b
+#define AO_PS2_SEMICOLON 0x4c
+#define AO_PS2_ACUTE 0x52
+#define AO_PS2_COMMA 0x41
+#define AO_PS2_PERIOD 0x49
+#define AO_PS2_SLASH 0x4a
+
+#define AO_PS2_RELEASE_FLAG 0x80
+
+#endif /* _AO_PS2_H_ */
diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c
index 4b17c5e3..45454000 100644
--- a/src/drivers/ao_sdcard.c
+++ b/src/drivers/ao_sdcard.c
@@ -38,13 +38,19 @@ extern uint8_t ao_radio_mutex;
#define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1)
/* Include SD card commands */
+#ifndef SDCARD_DEBUG
#define SDCARD_DEBUG 0
+#endif
/* Spew SD tracing */
+#ifndef SDCARD_TRACE
#define SDCARD_TRACE 0
+#endif
/* Emit error and warning messages */
+#ifndef SDCARD_WARN
#define SDCARD_WARN 0
+#endif
static uint8_t initialized;
static uint8_t present;
diff --git a/src/drivers/ao_vga.c b/src/drivers/ao_vga.c
new file mode 100644
index 00000000..909e3109
--- /dev/null
+++ b/src/drivers/ao_vga.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_vga.h"
+
+/* VGA output from the SPI port
+ *
+ * Connections:
+ *
+ * STM VGA
+ * GND 4,6,7,8,9,10
+ * HSYNC PA5 13
+ * VSYNC PB5 14
+ * RGB PB4 1,2,3
+ *
+ * pixel clock PA8 -> PB3
+ * pixel enable PA1 -> PA15
+ */
+
+/* GRF formula for 640x480 yields a pixel clock very close to 24MHz. Pad by
+ * three scanlines to hit exactly that value
+ */
+
+#define HACTIVE (640)
+#define HSYNC_START (656)
+#define HSYNC_END (720)
+#define HTOTAL (800)
+
+#define VACTIVE 480
+#define VSYNC_START 481
+#define VSYNC_END 484
+#define VTOTAL 500
+
+/*
+ * The horizontal counter is set so that the end of hsync is reached
+ * at the maximum counter value. That means that the hblank interval
+ * is offset by HSYNC_END.
+ */
+
+#define HSYNC (HSYNC_END - HSYNC_START)
+#define HBLANK_END (HTOTAL - HSYNC_END)
+#define HBLANK_START (HBLANK_END + HACTIVE)
+
+/*
+ * The vertical counter is set so that the end of vsync is reached at
+ * the maximum counter value. That means that the vblank interval is
+ * offset by VSYNC_END. We send a blank line at the start of the
+ * frame, so each of these is off by one
+ */
+#define VSYNC (VSYNC_END - VSYNC_START)
+#define VBLANK_END (VTOTAL - VSYNC_END)
+#define VBLANK_START (VBLANK_END + VACTIVE)
+
+#define WIDTH_BYTES (AO_VGA_WIDTH >> 3)
+#define SCANOUT ((WIDTH_BYTES+2) >> 1)
+
+uint32_t ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
+
+const struct ao_bitmap ao_vga_bitmap = {
+ .base = ao_vga_fb,
+ .stride = AO_VGA_STRIDE,
+ .width = AO_VGA_WIDTH,
+ .height = AO_VGA_HEIGHT
+};
+
+static uint32_t *scanline;
+
+#define DMA_INDEX STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
+
+#define DMA_CCR(en) ((0 << STM_DMA_CCR_MEM2MEM) | \
+ (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | \
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) | \
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) | \
+ (1 << STM_DMA_CCR_MINC) | \
+ (0 << STM_DMA_CCR_PINC) | \
+ (0 << STM_DMA_CCR_CIRC) | \
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR) | \
+ (0 << STM_DMA_CCR_TCIE) | \
+ (en << STM_DMA_CCR_EN))
+
+
+void stm_tim2_isr(void)
+{
+ int16_t line = stm_tim3.cnt;
+
+ if (VBLANK_END <= line && line < VBLANK_START) {
+ /* Disable */
+ stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(0);
+ /* Reset DMA engine for the next scanline */
+ stm_dma.channel[DMA_INDEX].cmar = scanline;
+ stm_dma.channel[DMA_INDEX].cndtr = SCANOUT;
+
+ /* reset SPI */
+ (void) stm_spi1.dr;
+ (void) stm_spi1.sr;
+
+ /* Enable */
+ stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
+ if (((line - VBLANK_END) & 1))
+ scanline += AO_VGA_STRIDE;
+ } else {
+ scanline = ao_vga_fb;
+ }
+ stm_tim2.sr = 0;
+}
+
+
+void
+ao_vga_init(void)
+{
+ uint32_t cfgr;
+
+ /* Initialize spi1 using MISO PB4 for output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF5);
+
+ /* turn on SPI */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+
+ stm_spi1.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) | /* Two wire mode */
+ (1 << STM_SPI_CR1_BIDIOE) |
+ (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
+ (0 << STM_SPI_CR1_CRCNEXT) |
+ (1 << STM_SPI_CR1_DFF) |
+ (0 << STM_SPI_CR1_RXONLY) | /* transmit, not receive */
+ (0 << STM_SPI_CR1_SSM) | /* Software SS handling */
+ (1 << STM_SPI_CR1_SSI) | /* ... */
+ (1 << STM_SPI_CR1_LSBFIRST) | /* Little endian */
+ (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
+ (0 << STM_SPI_CR1_BR) | /* baud rate to pclk/2 */
+ (0 << STM_SPI_CR1_MSTR) | /* slave */
+ (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
+ (0 << STM_SPI_CR1_CPHA));
+ stm_spi1.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+
+ (void) stm_spi1.dr;
+ (void) stm_spi1.sr;
+
+ /* Grab the DMA channel for SPI1 MOSI */
+ stm_dma.channel[DMA_INDEX].cpar = &stm_spi1.dr;
+ stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
+
+ /*
+ * Hsync Configuration
+ */
+ /* Turn on timer 2 */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
+
+ /* tim2 runs at full speed */
+ stm_tim2.psc = 0;
+
+ /* Disable channels while modifying */
+ stm_tim2.ccer = 0;
+
+ /* Channel 1 hsync PWM values */
+ stm_tim2.ccr1 = HSYNC;
+
+ /* Channel 2 trigger scanout */
+ /* wait for the time to start scanout */
+ stm_tim2.ccr2 = HBLANK_END;
+
+ stm_tim2.ccr3 = 32;
+
+ /* Configure channel 1 to output on the pin and
+ * channel 2 to to set the trigger for the vsync timer
+ */
+ stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+ (1 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+ (1 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+ stm_tim2.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+ (0 << STM_TIM234_CCMR2_OC4M) |
+ (0 << STM_TIM234_CCMR2_OC4PE) |
+ (0 << STM_TIM234_CCMR2_OC4FE) |
+ (0 << STM_TIM234_CCMR2_CC4S) |
+
+ (0 << STM_TIM234_CCMR2_OC3CE) |
+ (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
+ (1 << STM_TIM234_CCMR2_OC3PE) |
+ (0 << STM_TIM234_CCMR2_OC3FE) |
+ (0 << STM_TIM234_CCMR2_CC3S));
+
+ /* One scanline */
+ stm_tim2.arr = HTOTAL;
+
+ stm_tim2.cnt = 0;
+
+ /* Update the register contents */
+ stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
+
+ /* Enable the timer */
+
+ /* Enable the output */
+ stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC2NP) |
+ (STM_TIM234_CCER_CC2P_ACTIVE_HIGH << STM_TIM234_CCER_CC2P) |
+ (1 << STM_TIM234_CCER_CC2E) |
+ (0 << STM_TIM234_CCER_CC1NP) |
+ (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
+ (1 << STM_TIM234_CCER_CC1E));
+
+ stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ /* hsync is not a slave timer */
+ stm_tim2.smcr = 0;
+
+ /* Send an interrupt on channel 3 */
+ stm_tim2.dier = ((1 << STM_TIM234_DIER_CC3IE));
+
+ stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (1 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (1 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (0 << STM_TIM234_CR1_CEN));
+
+ /* Hsync is on PA5 which is Timer 2 CH1 output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+ stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
+
+ /* pixel transmit enable is on PA1 */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+ stm_ospeedr_set(&stm_gpioa, 1, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF1);
+
+ /*
+ * Vsync configuration
+ */
+
+ /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
+
+ /* No prescale */
+ stm_tim3.psc = 0;
+
+ /* Channel 1 or 2 vsync PWM values */
+ stm_tim3.ccr1 = VSYNC;
+ stm_tim3.ccr2 = VSYNC;
+
+ stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+ (1 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+ (1 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+ stm_tim3.arr = VTOTAL;
+ stm_tim3.cnt = 0;
+
+ /* Update the register contents */
+ stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
+
+ /* Enable the timer */
+
+ /* Enable the output */
+ stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
+ (STM_TIM234_CCER_CC2P_ACTIVE_LOW << STM_TIM234_CCER_CC2P) |
+ (1 << STM_TIM234_CCER_CC2E) |
+ (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
+ (1 << STM_TIM234_CCER_CC1E));
+
+ stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ stm_tim3.smcr = 0;
+ stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
+ (0 << STM_TIM234_SMCR_ECE) |
+ (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
+ (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
+ (0 << STM_TIM234_SMCR_MSM) |
+ (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
+ (0 << STM_TIM234_SMCR_OCCS) |
+ (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
+
+ stm_tim3.dier = 0;
+
+ stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (1 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (1 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (1 << STM_TIM234_CR1_CEN));
+
+ /* Vsync is on PB5 which is is Timer 3 CH2 output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+ stm_ospeedr_set(&stm_gpiob, 5, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpiob, 5, STM_AFR_AF2);
+
+ /* Use MCO for the pixel clock, that appears on PA8 */
+ cfgr = stm_rcc.cfgr & ~((STM_RCC_CFGR_MCOPRE_MASK << STM_RCC_CFGR_MCOPRE) |
+ (STM_RCC_CFGR_MCOSEL_MASK << STM_RCC_CFGR_MCOSEL));
+
+ cfgr |= ((STM_RCC_CFGR_MCOPRE_DIV_2 << STM_RCC_CFGR_MCOPRE) |
+ (STM_RCC_CFGR_MCOSEL_SYSCLK << STM_RCC_CFGR_MCOSEL));
+
+ stm_rcc.cfgr = cfgr;
+
+ stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+
+ /* Enable the scanline interrupt */
+ stm_nvic_set_priority(STM_ISR_TIM2_POS, AO_STM_NVIC_NONMASK_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_TIM2_POS);
+}
+
+uint8_t enabled;
+
+void
+ao_vga_enable(int enable)
+{
+ if (enable) {
+ if (!enabled) {
+ ++ao_task_minimize_latency;
+ enabled = 1;
+ }
+ stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
+ } else {
+ if (enabled) {
+ --ao_task_minimize_latency;
+ enabled = 0;
+ }
+ stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);
+ }
+}
diff --git a/src/drivers/ao_vga.h b/src/drivers/ao_vga.h
new file mode 100644
index 00000000..7d9d6b39
--- /dev/null
+++ b/src/drivers/ao_vga.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_VGA_H_
+#define _AO_VGA_H_
+
+#include "ao_draw.h"
+
+void
+ao_vga_init(void);
+
+void
+ao_vga_enable(int active);
+
+/* Active frame buffer */
+#define AO_VGA_WIDTH 320
+#define AO_VGA_HEIGHT 240
+
+/* Pad on the right so that there are zeros on the output after the line */
+#define AO_VGA_HPAD 32
+
+#define AO_VGA_STRIDE ((AO_VGA_WIDTH + AO_VGA_HPAD) >> AO_SHIFT)
+
+extern uint32_t ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
+
+extern const struct ao_bitmap ao_vga_bitmap;
+
+#endif /* _AO_VGA_H_ */
diff --git a/src/kernel/ao.h b/src/kernel/ao.h
index fb41d7a9..e56fbb2e 100644
--- a/src/kernel/ao.h
+++ b/src/kernel/ao.h
@@ -170,6 +170,9 @@ void
ao_put_string(__code char *s);
void
+ao_cmd_readline(void);
+
+char
ao_cmd_lex(void);
void
@@ -853,33 +856,6 @@ struct ao_fifo {
#include <ao_aes.h>
#endif
-/* ao_launch.c */
-
-struct ao_launch_command {
- uint16_t tick;
- uint16_t serial;
- uint8_t cmd;
- uint8_t channel;
- uint16_t unused;
-};
-
-#define AO_LAUNCH_QUERY 1
-
-struct ao_launch_query {
- uint16_t tick;
- uint16_t serial;
- uint8_t channel;
- uint8_t valid;
- uint8_t arm_status;
- uint8_t igniter_status;
-};
-
-#define AO_LAUNCH_ARM 2
-#define AO_LAUNCH_FIRE 3
-
-void
-ao_launch_init(void);
-
/*
* ao_log_single.c
*/
diff --git a/src/kernel/ao_cmd.c b/src/kernel/ao_cmd.c
index 10716afd..881f3500 100644
--- a/src/kernel/ao_cmd.c
+++ b/src/kernel/ao_cmd.c
@@ -24,13 +24,15 @@ __pdata uint32_t ao_cmd_lex_u32;
__pdata char ao_cmd_lex_c;
__pdata enum ao_cmd_status ao_cmd_status;
+#ifndef AO_CMD_LEN
#if AO_PYRO_NUM
-#define CMD_LEN 128
+#define AO_CMD_LEN 128
#else
-#define CMD_LEN 48
+#define AO_CMD_LEN 48
+#endif
#endif
-static __xdata char cmd_line[CMD_LEN];
+static __xdata char cmd_line[AO_CMD_LEN];
static __pdata uint8_t cmd_len;
static __pdata uint8_t cmd_i;
@@ -48,8 +50,8 @@ backspace(void)
ao_put_string ("\010 \010");
}
-static void
-readline(void)
+void
+ao_cmd_readline(void)
{
char c;
if (ao_echo())
@@ -88,7 +90,7 @@ readline(void)
break;
}
- if (cmd_len >= CMD_LEN - 2)
+ if (cmd_len >= AO_CMD_LEN - 2)
continue;
cmd_line[cmd_len++] = c;
if (ao_echo())
@@ -99,12 +101,13 @@ readline(void)
cmd_i = 0;
}
-void
+char
ao_cmd_lex(void)
{
ao_cmd_lex_c = '\n';
if (cmd_i < cmd_len)
ao_cmd_lex_c = cmd_line[cmd_i++];
+ return ao_cmd_lex_c;
}
static void
@@ -307,7 +310,7 @@ version(void)
#endif
#endif
#if defined(AO_BOOT_APPLICATION_BASE) && defined(AO_BOOT_APPLICATION_BOUND)
- , (uint32_t) AO_BOOT_APPLICATION_BOUND - (uint32_t) AO_BOOT_APPLICATION_BASE
+ , (unsigned) ((uint32_t) AO_BOOT_APPLICATION_BOUND - (uint32_t) AO_BOOT_APPLICATION_BASE)
#endif
);
printf("software-version %s\n", ao_version);
@@ -376,7 +379,7 @@ ao_cmd(void)
void (*__xdata func)(void);
for (;;) {
- readline();
+ ao_cmd_readline();
ao_cmd_lex();
ao_cmd_white();
c = ao_cmd_lex_c;
diff --git a/src/kernel/ao_log.h b/src/kernel/ao_log.h
index 13eb05bf..a2f2c6ca 100644
--- a/src/kernel/ao_log.h
+++ b/src/kernel/ao_log.h
@@ -47,10 +47,12 @@ extern __pdata enum ao_flight_state ao_log_state;
#define AO_LOG_FORMAT_TELEMEGA_OLD 5 /* 32 byte typed telemega records */
#define AO_LOG_FORMAT_EASYMINI 6 /* 16-byte MS5607 baro only, 3.0V supply */
#define AO_LOG_FORMAT_TELEMETRUM 7 /* 16-byte typed telemetrum records */
-#define AO_LOG_FORMAT_TELEMINI 8 /* 16-byte MS5607 baro only, 3.3V supply */
+#define AO_LOG_FORMAT_TELEMINI2 8 /* 16-byte MS5607 baro only, 3.3V supply, cc1111 SoC */
#define AO_LOG_FORMAT_TELEGPS 9 /* 32 byte telegps records */
#define AO_LOG_FORMAT_TELEMEGA 10 /* 32 byte typed telemega records with 32 bit gyro cal */
#define AO_LOG_FORMAT_DETHERM 11 /* 16-byte MS5607 baro only, no ADC */
+#define AO_LOG_FORMAT_TELEMINI3 12 /* 16-byte MS5607 baro only, 3.3V supply, stm32f042 SoC */
+#define AO_LOG_FORMAT_TELEFIRETWO 13 /* 32-byte test stand data */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
@@ -299,6 +301,32 @@ struct ao_log_mega {
((l)->u.gps.altitude_high = (a) >> 16), \
(l)->u.gps.altitude_low = (a))
+struct ao_log_firetwo {
+ char type; /* 0 */
+ uint8_t csum; /* 1 */
+ uint16_t tick; /* 2 */
+ union { /* 4 */
+ /* AO_LOG_FLIGHT */
+ struct {
+ uint16_t flight; /* 4 */
+ uint16_t idle_pressure; /* 6 */
+ uint16_t idle_thrust; /* 8 */
+ } flight; /* 16 */
+ /* AO_LOG_STATE */
+ struct {
+ uint16_t state; /* 4 */
+ uint16_t reason; /* 6 */
+ } state; /* 8 */
+ /* AO_LOG_SENSOR */
+ struct {
+ uint16_t pressure; /* 4 */
+ uint16_t thrust; /* 6 */
+ uint16_t thermistor[4]; /* 8 */
+ } sensor; /* 24 */
+ uint8_t align[28]; /* 4 */
+ } u; /* 32 */
+};
+
struct ao_log_metrum {
char type; /* 0 */
uint8_t csum; /* 1 */
diff --git a/src/kernel/ao_log_firetwo.c b/src/kernel/ao_log_firetwo.c
new file mode 100644
index 00000000..4b42abe4
--- /dev/null
+++ b/src/kernel/ao_log_firetwo.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2017 Bdale Garbee <bdale@gag.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+static __xdata struct ao_log_firetwo log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEFIRETWO;
+
+static uint8_t
+ao_log_csum(__xdata uint8_t *b) __reentrant
+{
+ uint8_t sum = 0x5a;
+ uint8_t i;
+
+ for (i = 0; i < sizeof (struct ao_log_firetwo); i++)
+ sum += *b++;
+ return -sum;
+}
+
+uint8_t
+ao_log_firetwo(__xdata struct ao_log_firetwo *log) __reentrant
+{
+ uint8_t wrote = 0;
+ /* set checksum */
+ log->csum = 0;
+ log->csum = ao_log_csum((__xdata uint8_t *) log);
+ ao_mutex_get(&ao_log_mutex); {
+ if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+ ao_log_stop();
+ if (ao_log_running) {
+ wrote = 1;
+ ao_storage_write(ao_log_current_pos,
+ log,
+ sizeof (struct ao_log_firetwo));
+ ao_log_current_pos += sizeof (struct ao_log_firetwo);
+ }
+ } ao_mutex_put(&ao_log_mutex);
+ return wrote;
+}
+
+static uint8_t
+ao_log_dump_check_data(void)
+{
+ if (ao_log_csum((uint8_t *) &log) != 0)
+ return 0;
+ return 1;
+}
+
+#if HAS_ADC
+static __data uint8_t ao_log_data_pos;
+
+/* a hack to make sure that ao_log_metrums fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_firetwo))] ;
+#endif
+
+void
+ao_log(void)
+{
+ uint16_t ao_idle_pressure = 0; // write code to capture pre-test values someday
+ uint16_t ao_idle_thrust = 0;
+ uint16_t ao_flight_state = ao_flight_startup;
+
+ ao_storage_setup();
+
+ do {
+ ao_log_scan();
+
+ while (!ao_log_running)
+ ao_sleep(&ao_log_running);
+
+ log.type = AO_LOG_FLIGHT;
+ log.tick = ao_time();
+ log.u.flight.idle_pressure = ao_idle_pressure;
+ log.u.flight.idle_thrust = ao_idle_thrust;
+ log.u.flight.flight = ao_flight_number;
+ ao_log_firetwo(&log);
+
+ /* Write the whole contents of the ring to the log
+ * when starting up.
+ */
+ ao_log_data_pos = ao_data_ring_next(ao_data_head);
+ ao_log_state = ao_flight_startup;
+ for (;;) {
+ /* Write samples to EEPROM */
+ while (ao_log_data_pos != ao_data_head) {
+ log.tick = ao_data_ring[ao_log_data_pos].tick;
+ log.type = AO_LOG_SENSOR;
+ log.u.sensor.pressure = ao_data_ring[ao_log_data_pos].adc.pressure;
+ log.u.sensor.thrust = ao_data_ring[ao_log_data_pos].adc.thrust;
+ // for (i = 0; i < 4; i++) {
+ // log.u.sensor.thermistor[i] = ao_data_ring[ao_log_data_pos].sensor.thermistor[i];
+ // }
+ ao_log_firetwo(&log);
+ ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+ }
+ /* Write state change to EEPROM */
+ if (ao_flight_state != ao_log_state) {
+ ao_log_state = ao_flight_state;
+ log.type = AO_LOG_STATE;
+ log.tick = ao_time();
+ log.u.state.state = ao_log_state;
+ log.u.state.reason = 0;
+ ao_log_firetwo(&log);
+
+ if (ao_log_state == ao_flight_landed)
+ ao_log_stop();
+ }
+
+ ao_log_flush();
+
+ if (!ao_log_running) break;
+
+ /* Wait for a while */
+ ao_delay(AO_MS_TO_TICKS(100));
+ }
+ } while (ao_log_running);
+}
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+ if (!ao_storage_read(ao_log_pos(slot),
+ &log,
+ sizeof (struct ao_log_firetwo)))
+ return 0;
+
+ if (ao_log_dump_check_data() && log.type == AO_LOG_FLIGHT)
+ return log.u.flight.flight;
+ return 0;
+}
diff --git a/src/kernel/ao_pyro.c b/src/kernel/ao_pyro.c
index 813e866a..a0881f9e 100644
--- a/src/kernel/ao_pyro.c
+++ b/src/kernel/ao_pyro.c
@@ -75,7 +75,8 @@ uint16_t ao_pyro_fired;
#endif
#if PYRO_DBG
-#define DBG(...) do { printf("\t%d: ", (int) (pyro - ao_config.pyro)); printf(__VA_ARGS__); } while (0)
+int pyro_dbg;
+#define DBG(...) do { if (pyro_dbg) printf("\t%d: ", (int) (pyro - ao_config.pyro)); printf(__VA_ARGS__); } while (0)
#else
#define DBG(...)
#endif
diff --git a/src/kernel/ao_report.c b/src/kernel/ao_report.c
index 6592d616..af48b390 100644
--- a/src/kernel/ao_report.c
+++ b/src/kernel/ao_report.c
@@ -45,9 +45,16 @@ static const uint8_t flight_reports[] = {
#define mid(time) ao_beep_for(AO_BEEP_MID, time)
#define high(time) ao_beep_for(AO_BEEP_HIGH, time)
#else
-#define low(time) ao_led_for(AO_LED_GREEN, time)
-#define mid(time) ao_led_for(AO_LED_RED, time)
-#define high(time) ao_led_for(AO_LED_GREEN|AO_LED_RED, time)
+#ifndef AO_LED_LOW
+#define AO_LED_LOW AO_LED_GREEN
+#endif
+#ifndef AO_LED_MID
+#define AO_LED_MID AO_LED_RED
+#endif
+
+#define low(time) ao_led_for(AO_LED_LOW, time)
+#define mid(time) ao_led_for(AO_LED_MID, time)
+#define high(time) ao_led_for(AO_LED_MID|AO_LED_LOW, time)
#endif
#define pause(time) ao_delay(time)
diff --git a/src/kernel/ao_stdio.c b/src/kernel/ao_stdio.c
index b79d465a..f0ee0a14 100644
--- a/src/kernel/ao_stdio.c
+++ b/src/kernel/ao_stdio.c
@@ -55,6 +55,9 @@
#ifndef PACKET_HAS_SLAVE
#define PACKET_HAS_SLAVE 0
#endif
+#ifndef CONSOLE_STDIN
+#define CONSOLE_STDIN 0
+#endif
#define USE_SERIAL_STDIN (USE_SERIAL_0_STDIN + \
USE_SERIAL_1_STDIN + \
@@ -67,7 +70,7 @@
USE_SERIAL_8_STDIN + \
USE_SERIAL_9_STDIN)
-#define AO_NUM_STDIOS (HAS_USB + PACKET_HAS_SLAVE + USE_SERIAL_STDIN)
+#define AO_NUM_STDIOS (HAS_USB + PACKET_HAS_SLAVE + USE_SERIAL_STDIN + CONSOLE_STDIN)
__xdata struct ao_stdio ao_stdios[AO_NUM_STDIOS];
diff --git a/src/kernel/ao_task.c b/src/kernel/ao_task.c
index e8a092aa..de23ea02 100644
--- a/src/kernel/ao_task.c
+++ b/src/kernel/ao_task.c
@@ -373,7 +373,11 @@ ao_yield(void) ao_arch_naked_define
if (!ao_list_is_empty(&run_queue))
break;
/* Wait for interrupts when there's nothing ready */
- ao_arch_wait_interrupt();
+ if (ao_task_minimize_latency) {
+ ao_arch_release_interrupts();
+ ao_arch_block_interrupts();
+ } else
+ ao_arch_wait_interrupt();
}
ao_cur_task = ao_list_first_entry(&run_queue, struct ao_task, queue);
#else
diff --git a/src/kernel/ao_telemetry.c b/src/kernel/ao_telemetry.c
index 15085bf4..fa817824 100644
--- a/src/kernel/ao_telemetry.c
+++ b/src/kernel/ao_telemetry.c
@@ -270,7 +270,7 @@ ao_send_mini(void)
__xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
telemetry.generic.tick = packet->tick;
- telemetry.generic.type = AO_TELEMETRY_MINI;
+ telemetry.generic.type = AO_SEND_MINI;
telemetry.mini.state = ao_flight_state;
diff --git a/src/kernel/ao_telemetry.h b/src/kernel/ao_telemetry.h
index c0f5e3c5..45aaeb07 100644
--- a/src/kernel/ao_telemetry.h
+++ b/src/kernel/ao_telemetry.h
@@ -270,7 +270,8 @@ struct ao_telemetry_metrum_data {
/* 32 */
};
-#define AO_TELEMETRY_MINI 0x10
+#define AO_TELEMETRY_MINI2 0x10 /* CC1111 based */
+#define AO_TELEMETRY_MINI3 0x11 /* STMF042 based */
struct ao_telemetry_mini {
uint16_t serial; /* 0 */
diff --git a/src/lambdakey-v1.0/.gitignore b/src/lambdakey-v1.0/.gitignore
new file mode 100644
index 00000000..6462d930
--- /dev/null
+++ b/src/lambdakey-v1.0/.gitignore
@@ -0,0 +1,2 @@
+lambdakey-*
+ao_product.h
diff --git a/src/lambdakey-v1.0/Makefile b/src/lambdakey-v1.0/Makefile
new file mode 100644
index 00000000..2609bea3
--- /dev/null
+++ b/src/lambdakey-v1.0/Makefile
@@ -0,0 +1,92 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_lisp.h \
+ ao_lisp_const.h \
+ ao_lisp_os.h \
+ stm32f0.h \
+ Makefile
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_dma_stm.c \
+ ao_stdio.c \
+ ao_mutex.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_usb_stm.c \
+ ao_flash_stm.c \
+ ao_lisp_lex.c \
+ ao_lisp_mem.c \
+ ao_lisp_cons.c \
+ ao_lisp_eval.c \
+ ao_lisp_string.c \
+ ao_lisp_atom.c \
+ ao_lisp_int.c \
+ ao_lisp_poly.c \
+ ao_lisp_builtin.c \
+ ao_lisp_read.c \
+ ao_lisp_rep.c \
+ ao_lisp_frame.c \
+ ao_lisp_error.c \
+ ao_lisp_lambda.c \
+ ao_lisp_save.c \
+ ao_lisp_stack.c \
+ ao_lisp_os_save.c
+
+PRODUCT=LambdaKey-v1.0
+PRODUCT_DEF=-DLAMBDAKEY
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) -I. $(STMF0_CFLAGS) -Os -g
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tlambda.ld
+
+PROGNAME=lambdakey-v1.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_lambdakey.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) lambda.ld altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+load: $(PROG)
+ stm-load $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/lambdakey-v1.0/ao_lambdakey.c b/src/lambdakey-v1.0/ao_lambdakey.c
new file mode 100644
index 00000000..8bd344cf
--- /dev/null
+++ b/src/lambdakey-v1.0/ao_lambdakey.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_lisp.h>
+
+static void lisp_cmd() {
+ ao_lisp_read_eval_print();
+}
+
+static const struct ao_cmds blink_cmds[] = {
+ { lisp_cmd, "l\0Run lisp interpreter" },
+ { 0, 0 }
+};
+
+
+void main(void)
+{
+ ao_led_init(LEDS_AVAILABLE);
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+ ao_dma_init();
+ ao_usb_init();
+ ao_cmd_init();
+ ao_cmd_register(blink_cmds);
+ ao_start_scheduler();
+}
+
+
diff --git a/src/lambdakey-v1.0/ao_lisp_os.h b/src/lambdakey-v1.0/ao_lisp_os.h
new file mode 100644
index 00000000..1993ac44
--- /dev/null
+++ b/src/lambdakey-v1.0/ao_lisp_os.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LISP_OS_H_
+#define _AO_LISP_OS_H_
+
+#include "ao.h"
+
+static inline int
+ao_lisp_getc() {
+ static uint8_t at_eol;
+ int c;
+
+ if (at_eol) {
+ ao_cmd_readline();
+ at_eol = 0;
+ }
+ c = ao_cmd_lex();
+ if (c == '\n')
+ at_eol = 1;
+ return c;
+}
+
+static inline void
+ao_lisp_os_flush(void)
+{
+ flush();
+}
+
+static inline void
+ao_lisp_abort(void)
+{
+ ao_panic(1);
+}
+
+static inline void
+ao_lisp_os_led(int led)
+{
+ ao_led_set(led);
+}
+
+static inline void
+ao_lisp_os_delay(int delay)
+{
+ ao_delay(AO_MS_TO_TICKS(delay));
+}
+
+#endif
diff --git a/src/lambdakey-v1.0/ao_lisp_os_save.c b/src/lambdakey-v1.0/ao_lisp_os_save.c
new file mode 100644
index 00000000..44138398
--- /dev/null
+++ b/src/lambdakey-v1.0/ao_lisp_os_save.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_lisp.h>
+#include <ao_flash.h>
+
+extern uint8_t __flash__[];
+
+/* saved variables to rebuild the heap
+
+ ao_lisp_atoms
+ ao_lisp_frame_global
+ */
+
+int
+ao_lisp_os_save(void)
+{
+ int i;
+
+ for (i = 0; i < AO_LISP_POOL_TOTAL; i += 256) {
+ uint32_t *dst = (uint32_t *) &__flash__[i];
+ uint32_t *src = (uint32_t *) &ao_lisp_pool[i];
+
+ ao_flash_page(dst, src);
+ }
+ return 1;
+}
+
+int
+ao_lisp_os_restore_save(struct ao_lisp_os_save *save, int offset)
+{
+ memcpy(save, &__flash__[offset], sizeof (struct ao_lisp_os_save));
+ return 1;
+}
+
+int
+ao_lisp_os_restore(void)
+{
+ memcpy(ao_lisp_pool, __flash__, AO_LISP_POOL_TOTAL);
+ return 1;
+}
diff --git a/src/lambdakey-v1.0/ao_pins.h b/src/lambdakey-v1.0/ao_pins.h
new file mode 100644
index 00000000..2ba79c01
--- /dev/null
+++ b/src/lambdakey-v1.0/ao_pins.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPBEN
+#define LED_PORT (&stm_gpiob)
+#define LED_PIN_RED 4
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_PANIC AO_LED_RED
+#define AO_CMD_LEN 128
+#define AO_LISP_POOL_TOTAL 3072
+#define AO_LISP_SAVE 1
+#define AO_STACK_SIZE 1024
+
+/* need HSI active to write to flash */
+#define AO_NEED_HSI 1
+
+#define LEDS_AVAILABLE (AO_LED_RED)
+
+#define AO_POWER_MANAGEMENT 0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+#define HAS_BEEP 0
+
+#define IS_FLASH_LOADER 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/lambdakey-v1.0/flash-loader/.gitignore b/src/lambdakey-v1.0/flash-loader/.gitignore
new file mode 100644
index 00000000..86ebb7f2
--- /dev/null
+++ b/src/lambdakey-v1.0/flash-loader/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+lambdakey*
diff --git a/src/lambdakey-v1.0/flash-loader/Makefile b/src/lambdakey-v1.0/flash-loader/Makefile
new file mode 100644
index 00000000..dbded719
--- /dev/null
+++ b/src/lambdakey-v1.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=lambdakey-v1.0
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/lambdakey-v1.0/flash-loader/ao_pins.h b/src/lambdakey-v1.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..4b788f67
--- /dev/null
+++ b/src/lambdakey-v1.0/flash-loader/ao_pins.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin 5 on debug connector */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 15
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/lambdakey-v1.0/lambda.ld b/src/lambdakey-v1.0/lambda.ld
new file mode 100644
index 00000000..5de65eb5
--- /dev/null
+++ b/src/lambdakey-v1.0/lambda.ld
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08001000, LENGTH = 25K
+ flash (r): ORIGIN = 0x08007400, LENGTH = 3k
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 6k - 128
+ stack (!w) : ORIGIN = 0x20000000 + 6k - 128, LENGTH = 128
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .interrupt ORIGIN(ram) : AT (ORIGIN(rom)) {
+ __interrupt_start__ = .;
+ __interrupt_rom__ = ORIGIN(rom);
+ *(.interrupt) /* Interrupt vectors */
+ __interrupt_end__ = .;
+ } > ram
+
+ .text ORIGIN(rom) + 0x100 : {
+ __text_start__ = .;
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+
+ *(.text*) /* Executable code */
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ *(.rodata*) /* Constants */
+
+ } > rom
+ __text_end__ = .;
+
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ . = ALIGN(4);
+ __boot_end__ = .;
+ } >ram
+
+ /* Functions placed in RAM (required for flashing)
+ *
+ * Align to 8 bytes as that's what the ARM likes text
+ * segment alignments to be, and if we don't, then
+ * we end up with a mismatch between the location in
+ * ROM and the desired location in RAM. I don't
+ * entirely understand this, but at least this appears
+ * to work...
+ */
+
+ .textram BLOCK(8): {
+ __data_start__ = .;
+ __text_ram_start__ = .;
+ *(.ramtext)
+ __text_ram_end = .;
+ } >ram AT>rom
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+
+ __flash__ = ORIGIN(flash);
+}
+
+ENTRY(start);
diff --git a/src/lisp/.gitignore b/src/lisp/.gitignore
new file mode 100644
index 00000000..76a555ea
--- /dev/null
+++ b/src/lisp/.gitignore
@@ -0,0 +1,2 @@
+ao_lisp_make_const
+ao_lisp_const.h
diff --git a/src/lisp/Makefile b/src/lisp/Makefile
new file mode 100644
index 00000000..25796ec5
--- /dev/null
+++ b/src/lisp/Makefile
@@ -0,0 +1,22 @@
+all: ao_lisp_const.h
+
+clean:
+ rm -f ao_lisp_const.h $(OBJS) ao_lisp_make_const
+
+ao_lisp_const.h: ao_lisp_const.lisp ao_lisp_make_const
+ ./ao_lisp_make_const -o $@ ao_lisp_const.lisp
+
+include Makefile-inc
+SRCS=$(LISP_SRCS)
+
+HDRS=$(LISP_HDRS)
+
+OBJS=$(SRCS:.c=.o)
+
+CFLAGS=-DAO_LISP_MAKE_CONST -O0 -g -I. -Wall -Wextra -no-pie
+
+
+ao_lisp_make_const: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS)
+
+$(OBJS): $(HDRS)
diff --git a/src/lisp/Makefile-inc b/src/lisp/Makefile-inc
new file mode 100644
index 00000000..126deeb0
--- /dev/null
+++ b/src/lisp/Makefile-inc
@@ -0,0 +1,22 @@
+LISP_SRCS=\
+ ao_lisp_make_const.c\
+ ao_lisp_mem.c \
+ ao_lisp_cons.c \
+ ao_lisp_string.c \
+ ao_lisp_atom.c \
+ ao_lisp_int.c \
+ ao_lisp_poly.c \
+ ao_lisp_builtin.c \
+ ao_lisp_read.c \
+ ao_lisp_frame.c \
+ ao_lisp_lambda.c \
+ ao_lisp_eval.c \
+ ao_lisp_rep.c \
+ ao_lisp_save.c \
+ ao_lisp_stack.c \
+ ao_lisp_error.c
+
+LISP_HDRS=\
+ ao_lisp.h \
+ ao_lisp_os.h \
+ ao_lisp_read.h
diff --git a/src/lisp/Makefile-lisp b/src/lisp/Makefile-lisp
new file mode 100644
index 00000000..998c7673
--- /dev/null
+++ b/src/lisp/Makefile-lisp
@@ -0,0 +1,4 @@
+include ../lisp/Makefile-inc
+
+ao_lisp_const.h: $(LISP_SRCS) $(LISP_HDRS)
+ +cd ../lisp && make $@
diff --git a/src/lisp/ao_lisp.h b/src/lisp/ao_lisp.h
new file mode 100644
index 00000000..980514cc
--- /dev/null
+++ b/src/lisp/ao_lisp.h
@@ -0,0 +1,793 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_LISP_H_
+#define _AO_LISP_H_
+
+#define DBG_MEM 0
+#define DBG_EVAL 0
+
+#include <stdint.h>
+#include <string.h>
+#include <ao_lisp_os.h>
+
+typedef uint16_t ao_poly;
+typedef int16_t ao_signed_poly;
+
+#ifdef AO_LISP_SAVE
+
+struct ao_lisp_os_save {
+ ao_poly atoms;
+ ao_poly globals;
+ uint16_t const_checksum;
+ uint16_t const_checksum_inv;
+};
+
+#define AO_LISP_POOL_EXTRA (sizeof(struct ao_lisp_os_save))
+#define AO_LISP_POOL ((int) (AO_LISP_POOL_TOTAL - AO_LISP_POOL_EXTRA))
+
+int
+ao_lisp_os_save(void);
+
+int
+ao_lisp_os_restore_save(struct ao_lisp_os_save *save, int offset);
+
+int
+ao_lisp_os_restore(void);
+
+#endif
+
+#ifdef AO_LISP_MAKE_CONST
+#define AO_LISP_POOL_CONST 16384
+extern uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute__((aligned(4)));
+#define ao_lisp_pool ao_lisp_const
+#define AO_LISP_POOL AO_LISP_POOL_CONST
+
+#define _atom(n) ao_lisp_atom_poly(ao_lisp_atom_intern(n))
+
+#define _ao_lisp_atom_quote _atom("quote")
+#define _ao_lisp_atom_set _atom("set")
+#define _ao_lisp_atom_setq _atom("setq")
+#define _ao_lisp_atom_t _atom("t")
+#define _ao_lisp_atom_car _atom("car")
+#define _ao_lisp_atom_cdr _atom("cdr")
+#define _ao_lisp_atom_cons _atom("cons")
+#define _ao_lisp_atom_last _atom("last")
+#define _ao_lisp_atom_length _atom("length")
+#define _ao_lisp_atom_cond _atom("cond")
+#define _ao_lisp_atom_lambda _atom("lambda")
+#define _ao_lisp_atom_led _atom("led")
+#define _ao_lisp_atom_delay _atom("delay")
+#define _ao_lisp_atom_pack _atom("pack")
+#define _ao_lisp_atom_unpack _atom("unpack")
+#define _ao_lisp_atom_flush _atom("flush")
+#define _ao_lisp_atom_eval _atom("eval")
+#define _ao_lisp_atom_read _atom("read")
+#define _ao_lisp_atom_eof _atom("eof")
+#define _ao_lisp_atom_save _atom("save")
+#define _ao_lisp_atom_restore _atom("restore")
+#define _ao_lisp_atom_call2fcc _atom("call/cc")
+#define _ao_lisp_atom_collect _atom("collect")
+#define _ao_lisp_atom_symbolp _atom("symbol?")
+#define _ao_lisp_atom_builtin _atom("builtin?")
+#define _ao_lisp_atom_symbolp _atom("symbol?")
+#define _ao_lisp_atom_symbolp _atom("symbol?")
+#else
+#include "ao_lisp_const.h"
+#ifndef AO_LISP_POOL
+#define AO_LISP_POOL 3072
+#endif
+extern uint8_t ao_lisp_pool[AO_LISP_POOL + AO_LISP_POOL_EXTRA] __attribute__((aligned(4)));
+#endif
+
+/* Primitive types */
+#define AO_LISP_CONS 0
+#define AO_LISP_INT 1
+#define AO_LISP_STRING 2
+#define AO_LISP_OTHER 3
+
+#define AO_LISP_TYPE_MASK 0x0003
+#define AO_LISP_TYPE_SHIFT 2
+#define AO_LISP_REF_MASK 0x7ffc
+#define AO_LISP_CONST 0x8000
+
+/* These have a type value at the start of the struct */
+#define AO_LISP_ATOM 4
+#define AO_LISP_BUILTIN 5
+#define AO_LISP_FRAME 6
+#define AO_LISP_LAMBDA 7
+#define AO_LISP_STACK 8
+#define AO_LISP_NUM_TYPE 9
+
+/* Leave two bits for types to use as they please */
+#define AO_LISP_OTHER_TYPE_MASK 0x3f
+
+#define AO_LISP_NIL 0
+
+extern uint16_t ao_lisp_top;
+
+#define AO_LISP_OOM 0x01
+#define AO_LISP_DIVIDE_BY_ZERO 0x02
+#define AO_LISP_INVALID 0x04
+#define AO_LISP_UNDEFINED 0x08
+#define AO_LISP_EOF 0x10
+
+extern uint8_t ao_lisp_exception;
+
+static inline int
+ao_lisp_is_const(ao_poly poly) {
+ return poly & AO_LISP_CONST;
+}
+
+#define AO_LISP_IS_CONST(a) (ao_lisp_const <= ((uint8_t *) (a)) && ((uint8_t *) (a)) < ao_lisp_const + AO_LISP_POOL_CONST)
+#define AO_LISP_IS_POOL(a) (ao_lisp_pool <= ((uint8_t *) (a)) && ((uint8_t *) (a)) < ao_lisp_pool + AO_LISP_POOL)
+#define AO_LISP_IS_INT(p) (ao_lisp_base_type(p) == AO_LISP_INT);
+
+void *
+ao_lisp_ref(ao_poly poly);
+
+ao_poly
+ao_lisp_poly(const void *addr, ao_poly type);
+
+struct ao_lisp_type {
+ int (*size)(void *addr);
+ void (*mark)(void *addr);
+ void (*move)(void *addr);
+ char name[];
+};
+
+struct ao_lisp_cons {
+ ao_poly car;
+ ao_poly cdr;
+};
+
+struct ao_lisp_atom {
+ uint8_t type;
+ uint8_t pad[1];
+ ao_poly next;
+ char name[];
+};
+
+struct ao_lisp_val {
+ ao_poly atom;
+ ao_poly val;
+};
+
+struct ao_lisp_frame {
+ uint8_t type;
+ uint8_t num;
+ ao_poly prev;
+ struct ao_lisp_val vals[];
+};
+
+/* Set on type when the frame escapes the lambda */
+#define AO_LISP_FRAME_MARK 0x80
+#define AO_LISP_FRAME_PRINT 0x40
+
+static inline int ao_lisp_frame_marked(struct ao_lisp_frame *f) {
+ return f->type & AO_LISP_FRAME_MARK;
+}
+
+static inline struct ao_lisp_frame *
+ao_lisp_poly_frame(ao_poly poly) {
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_frame_poly(struct ao_lisp_frame *frame) {
+ return ao_lisp_poly(frame, AO_LISP_OTHER);
+}
+
+enum eval_state {
+ eval_sexpr, /* Evaluate an sexpr */
+ eval_val, /* Value computed */
+ eval_formal, /* Formal computed */
+ eval_exec, /* Start a lambda evaluation */
+ eval_cond, /* Start next cond clause */
+ eval_cond_test, /* Check cond condition */
+ eval_progn, /* Start next progn entry */
+ eval_while, /* Start while condition */
+ eval_while_test, /* Check while condition */
+ eval_macro, /* Finished with macro generation */
+};
+
+struct ao_lisp_stack {
+ uint8_t type; /* AO_LISP_STACK */
+ uint8_t state; /* enum eval_state */
+ ao_poly prev; /* previous stack frame */
+ ao_poly sexprs; /* expressions to evaluate */
+ ao_poly values; /* values computed */
+ ao_poly values_tail; /* end of the values list for easy appending */
+ ao_poly frame; /* current lookup frame */
+ ao_poly list; /* most recent function call */
+};
+
+#define AO_LISP_STACK_MARK 0x80 /* set on type when a reference has been taken */
+#define AO_LISP_STACK_PRINT 0x40 /* stack is being printed */
+
+static inline int ao_lisp_stack_marked(struct ao_lisp_stack *s) {
+ return s->type & AO_LISP_STACK_MARK;
+}
+
+static inline void ao_lisp_stack_mark(struct ao_lisp_stack *s) {
+ s->type |= AO_LISP_STACK_MARK;
+}
+
+static inline struct ao_lisp_stack *
+ao_lisp_poly_stack(ao_poly p)
+{
+ return ao_lisp_ref(p);
+}
+
+static inline ao_poly
+ao_lisp_stack_poly(struct ao_lisp_stack *stack)
+{
+ return ao_lisp_poly(stack, AO_LISP_OTHER);
+}
+
+extern ao_poly ao_lisp_v;
+
+#define AO_LISP_FUNC_LAMBDA 0
+#define AO_LISP_FUNC_NLAMBDA 1
+#define AO_LISP_FUNC_MACRO 2
+#define AO_LISP_FUNC_LEXPR 3
+
+#define AO_LISP_FUNC_FREE_ARGS 0x80
+#define AO_LISP_FUNC_MASK 0x7f
+
+#define AO_LISP_FUNC_F_LAMBDA (AO_LISP_FUNC_FREE_ARGS | AO_LISP_FUNC_LAMBDA)
+#define AO_LISP_FUNC_F_NLAMBDA (AO_LISP_FUNC_FREE_ARGS | AO_LISP_FUNC_NLAMBDA)
+#define AO_LISP_FUNC_F_MACRO (AO_LISP_FUNC_FREE_ARGS | AO_LISP_FUNC_MACRO)
+#define AO_LISP_FUNC_F_LEXPR (AO_LISP_FUNC_FREE_ARGS | AO_LISP_FUNC_LEXPR)
+
+struct ao_lisp_builtin {
+ uint8_t type;
+ uint8_t args;
+ uint16_t func;
+};
+
+enum ao_lisp_builtin_id {
+ builtin_eval,
+ builtin_read,
+ builtin_lambda,
+ builtin_lexpr,
+ builtin_nlambda,
+ builtin_macro,
+ builtin_car,
+ builtin_cdr,
+ builtin_cons,
+ builtin_last,
+ builtin_length,
+ builtin_quote,
+ builtin_set,
+ builtin_setq,
+ builtin_cond,
+ builtin_progn,
+ builtin_while,
+ builtin_print,
+ builtin_patom,
+ builtin_plus,
+ builtin_minus,
+ builtin_times,
+ builtin_divide,
+ builtin_mod,
+ builtin_equal,
+ builtin_less,
+ builtin_greater,
+ builtin_less_equal,
+ builtin_greater_equal,
+ builtin_pack,
+ builtin_unpack,
+ builtin_flush,
+ builtin_delay,
+ builtin_led,
+ builtin_save,
+ builtin_restore,
+ builtin_call_cc,
+ builtin_collect,
+ _builtin_last
+};
+
+typedef ao_poly (*ao_lisp_func_t)(struct ao_lisp_cons *cons);
+
+extern const ao_lisp_func_t ao_lisp_builtins[];
+
+static inline ao_lisp_func_t
+ao_lisp_func(struct ao_lisp_builtin *b)
+{
+ return ao_lisp_builtins[b->func];
+}
+
+struct ao_lisp_lambda {
+ uint8_t type;
+ uint8_t args;
+ ao_poly code;
+ ao_poly frame;
+};
+
+static inline struct ao_lisp_lambda *
+ao_lisp_poly_lambda(ao_poly poly)
+{
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_lambda_poly(struct ao_lisp_lambda *lambda)
+{
+ return ao_lisp_poly(lambda, AO_LISP_OTHER);
+}
+
+static inline void *
+ao_lisp_poly_other(ao_poly poly) {
+ return ao_lisp_ref(poly);
+}
+
+static inline uint8_t
+ao_lisp_other_type(void *other) {
+#if DBG_MEM
+ if ((*((uint8_t *) other) & AO_LISP_OTHER_TYPE_MASK) >= AO_LISP_NUM_TYPE)
+ ao_lisp_abort();
+#endif
+ return *((uint8_t *) other) & AO_LISP_OTHER_TYPE_MASK;
+}
+
+static inline ao_poly
+ao_lisp_other_poly(const void *other)
+{
+ return ao_lisp_poly(other, AO_LISP_OTHER);
+}
+
+static inline int
+ao_lisp_size_round(int size)
+{
+ return (size + 3) & ~3;
+}
+
+static inline int
+ao_lisp_size(const struct ao_lisp_type *type, void *addr)
+{
+ return ao_lisp_size_round(type->size(addr));
+}
+
+#define AO_LISP_OTHER_POLY(other) ((ao_poly)(other) + AO_LISP_OTHER)
+
+static inline int ao_lisp_poly_base_type(ao_poly poly) {
+ return poly & AO_LISP_TYPE_MASK;
+}
+
+static inline int ao_lisp_poly_type(ao_poly poly) {
+ int type = poly & AO_LISP_TYPE_MASK;
+ if (type == AO_LISP_OTHER)
+ return ao_lisp_other_type(ao_lisp_poly_other(poly));
+ return type;
+}
+
+static inline struct ao_lisp_cons *
+ao_lisp_poly_cons(ao_poly poly)
+{
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_cons_poly(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_poly(cons, AO_LISP_CONS);
+}
+
+static inline int
+ao_lisp_poly_int(ao_poly poly)
+{
+ return (int) ((ao_signed_poly) poly >> AO_LISP_TYPE_SHIFT);
+}
+
+static inline ao_poly
+ao_lisp_int_poly(int i)
+{
+ return ((ao_poly) i << 2) | AO_LISP_INT;
+}
+
+static inline char *
+ao_lisp_poly_string(ao_poly poly)
+{
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_string_poly(char *s)
+{
+ return ao_lisp_poly(s, AO_LISP_STRING);
+}
+
+static inline struct ao_lisp_atom *
+ao_lisp_poly_atom(ao_poly poly)
+{
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_atom_poly(struct ao_lisp_atom *a)
+{
+ return ao_lisp_poly(a, AO_LISP_OTHER);
+}
+
+static inline struct ao_lisp_builtin *
+ao_lisp_poly_builtin(ao_poly poly)
+{
+ return ao_lisp_ref(poly);
+}
+
+static inline ao_poly
+ao_lisp_builtin_poly(struct ao_lisp_builtin *b)
+{
+ return ao_lisp_poly(b, AO_LISP_OTHER);
+}
+
+/* memory functions */
+
+extern int ao_lisp_collects[2];
+extern int ao_lisp_freed[2];
+extern int ao_lisp_loops[2];
+
+/* returns 1 if the object was already marked */
+int
+ao_lisp_mark(const struct ao_lisp_type *type, void *addr);
+
+/* returns 1 if the object was already marked */
+int
+ao_lisp_mark_memory(const struct ao_lisp_type *type, void *addr);
+
+void *
+ao_lisp_move_map(void *addr);
+
+/* returns 1 if the object was already moved */
+int
+ao_lisp_move(const struct ao_lisp_type *type, void **ref);
+
+/* returns 1 if the object was already moved */
+int
+ao_lisp_move_memory(const struct ao_lisp_type *type, void **ref);
+
+void *
+ao_lisp_alloc(int size);
+
+#define AO_LISP_COLLECT_FULL 1
+#define AO_LISP_COLLECT_INCREMENTAL 0
+
+int
+ao_lisp_collect(uint8_t style);
+
+void
+ao_lisp_cons_stash(int id, struct ao_lisp_cons *cons);
+
+struct ao_lisp_cons *
+ao_lisp_cons_fetch(int id);
+
+void
+ao_lisp_poly_stash(int id, ao_poly poly);
+
+ao_poly
+ao_lisp_poly_fetch(int id);
+
+void
+ao_lisp_string_stash(int id, char *string);
+
+char *
+ao_lisp_string_fetch(int id);
+
+static inline void
+ao_lisp_stack_stash(int id, struct ao_lisp_stack *stack) {
+ ao_lisp_poly_stash(id, ao_lisp_stack_poly(stack));
+}
+
+static inline struct ao_lisp_stack *
+ao_lisp_stack_fetch(int id) {
+ return ao_lisp_poly_stack(ao_lisp_poly_fetch(id));
+}
+
+/* cons */
+extern const struct ao_lisp_type ao_lisp_cons_type;
+
+struct ao_lisp_cons *
+ao_lisp_cons_cons(ao_poly car, struct ao_lisp_cons *cdr);
+
+extern struct ao_lisp_cons *ao_lisp_cons_free_list;
+
+void
+ao_lisp_cons_free(struct ao_lisp_cons *cons);
+
+void
+ao_lisp_cons_print(ao_poly);
+
+void
+ao_lisp_cons_patom(ao_poly);
+
+int
+ao_lisp_cons_length(struct ao_lisp_cons *cons);
+
+/* string */
+extern const struct ao_lisp_type ao_lisp_string_type;
+
+char *
+ao_lisp_string_copy(char *a);
+
+char *
+ao_lisp_string_cat(char *a, char *b);
+
+ao_poly
+ao_lisp_string_pack(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_string_unpack(char *a);
+
+void
+ao_lisp_string_print(ao_poly s);
+
+void
+ao_lisp_string_patom(ao_poly s);
+
+/* atom */
+extern const struct ao_lisp_type ao_lisp_atom_type;
+
+extern struct ao_lisp_atom *ao_lisp_atoms;
+extern struct ao_lisp_frame *ao_lisp_frame_global;
+extern struct ao_lisp_frame *ao_lisp_frame_current;
+
+void
+ao_lisp_atom_print(ao_poly a);
+
+struct ao_lisp_atom *
+ao_lisp_atom_intern(char *name);
+
+ao_poly *
+ao_lisp_atom_ref(struct ao_lisp_frame *frame, ao_poly atom);
+
+ao_poly
+ao_lisp_atom_get(ao_poly atom);
+
+ao_poly
+ao_lisp_atom_set(ao_poly atom, ao_poly val);
+
+/* int */
+void
+ao_lisp_int_print(ao_poly i);
+
+/* prim */
+void
+ao_lisp_poly_print(ao_poly p);
+
+void
+ao_lisp_poly_patom(ao_poly p);
+
+int
+ao_lisp_poly_mark(ao_poly p, uint8_t note_cons);
+
+/* returns 1 if the object has already been moved */
+int
+ao_lisp_poly_move(ao_poly *p, uint8_t note_cons);
+
+/* eval */
+
+void
+ao_lisp_eval_clear_globals(void);
+
+int
+ao_lisp_eval_restart(void);
+
+ao_poly
+ao_lisp_eval(ao_poly p);
+
+ao_poly
+ao_lisp_set_cond(struct ao_lisp_cons *cons);
+
+/* builtin */
+void
+ao_lisp_builtin_print(ao_poly b);
+
+extern const struct ao_lisp_type ao_lisp_builtin_type;
+
+/* Check argument count */
+ao_poly
+ao_lisp_check_argc(ao_poly name, struct ao_lisp_cons *cons, int min, int max);
+
+/* Check argument type */
+ao_poly
+ao_lisp_check_argt(ao_poly name, struct ao_lisp_cons *cons, int argc, int type, int nil_ok);
+
+/* Fetch an arg (nil if off the end) */
+ao_poly
+ao_lisp_arg(struct ao_lisp_cons *cons, int argc);
+
+char *
+ao_lisp_args_name(uint8_t args);
+
+/* read */
+extern struct ao_lisp_cons *ao_lisp_read_cons;
+extern struct ao_lisp_cons *ao_lisp_read_cons_tail;
+extern struct ao_lisp_cons *ao_lisp_read_stack;
+
+ao_poly
+ao_lisp_read(void);
+
+/* rep */
+ao_poly
+ao_lisp_read_eval_print(void);
+
+/* frame */
+extern const struct ao_lisp_type ao_lisp_frame_type;
+
+#define AO_LISP_FRAME_FREE 6
+
+extern struct ao_lisp_frame *ao_lisp_frame_free_list[AO_LISP_FRAME_FREE];
+
+ao_poly
+ao_lisp_frame_mark(struct ao_lisp_frame *frame);
+
+ao_poly *
+ao_lisp_frame_ref(struct ao_lisp_frame *frame, ao_poly atom);
+
+struct ao_lisp_frame *
+ao_lisp_frame_new(int num);
+
+void
+ao_lisp_frame_free(struct ao_lisp_frame *frame);
+
+void
+ao_lisp_frame_bind(struct ao_lisp_frame *frame, int num, ao_poly atom, ao_poly val);
+
+int
+ao_lisp_frame_add(struct ao_lisp_frame **frame, ao_poly atom, ao_poly val);
+
+void
+ao_lisp_frame_print(ao_poly p);
+
+/* lambda */
+extern const struct ao_lisp_type ao_lisp_lambda_type;
+
+extern const char *ao_lisp_state_names[];
+
+struct ao_lisp_lambda *
+ao_lisp_lambda_new(ao_poly cons);
+
+void
+ao_lisp_lambda_print(ao_poly lambda);
+
+ao_poly
+ao_lisp_lambda(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_lexpr(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_nlambda(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_macro(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_lambda_eval(void);
+
+/* save */
+
+ao_poly
+ao_lisp_save(struct ao_lisp_cons *cons);
+
+ao_poly
+ao_lisp_restore(struct ao_lisp_cons *cons);
+
+/* stack */
+
+extern const struct ao_lisp_type ao_lisp_stack_type;
+extern struct ao_lisp_stack *ao_lisp_stack;
+extern struct ao_lisp_stack *ao_lisp_stack_free_list;
+
+void
+ao_lisp_stack_reset(struct ao_lisp_stack *stack);
+
+int
+ao_lisp_stack_push(void);
+
+void
+ao_lisp_stack_pop(void);
+
+void
+ao_lisp_stack_clear(void);
+
+void
+ao_lisp_stack_print(ao_poly stack);
+
+ao_poly
+ao_lisp_stack_eval(void);
+
+ao_poly
+ao_lisp_call_cc(struct ao_lisp_cons *cons);
+
+/* error */
+
+void
+ao_lisp_error_poly(char *name, ao_poly poly, ao_poly last);
+
+void
+ao_lisp_error_frame(int indent, char *name, struct ao_lisp_frame *frame);
+
+ao_poly
+ao_lisp_error(int error, char *format, ...);
+
+/* debugging macros */
+
+#if DBG_EVAL
+#define DBG_CODE 1
+int ao_lisp_stack_depth;
+#define DBG_DO(a) a
+#define DBG_INDENT() do { int _s; for(_s = 0; _s < ao_lisp_stack_depth; _s++) printf(" "); } while(0)
+#define DBG_IN() (++ao_lisp_stack_depth)
+#define DBG_OUT() (--ao_lisp_stack_depth)
+#define DBG_RESET() (ao_lisp_stack_depth = 0)
+#define DBG(...) printf(__VA_ARGS__)
+#define DBGI(...) do { DBG("%4d: ", __LINE__); DBG_INDENT(); DBG(__VA_ARGS__); } while (0)
+#define DBG_CONS(a) ao_lisp_cons_print(ao_lisp_cons_poly(a))
+#define DBG_POLY(a) ao_lisp_poly_print(a)
+#define OFFSET(a) ((a) ? (int) ((uint8_t *) a - ao_lisp_pool) : -1)
+#define DBG_STACK() ao_lisp_stack_print(ao_lisp_stack_poly(ao_lisp_stack))
+static inline void
+ao_lisp_frames_dump(void)
+{
+ struct ao_lisp_stack *s;
+ DBGI(".. current frame: "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ for (s = ao_lisp_stack; s; s = ao_lisp_poly_stack(s->prev)) {
+ DBGI(".. stack frame: "); DBG_POLY(s->frame); DBG("\n");
+ }
+}
+#define DBG_FRAMES() ao_lisp_frames_dump()
+#else
+#define DBG_DO(a)
+#define DBG_INDENT()
+#define DBG_IN()
+#define DBG_OUT()
+#define DBG(...)
+#define DBGI(...)
+#define DBG_CONS(a)
+#define DBG_POLY(a)
+#define DBG_RESET()
+#define DBG_STACK()
+#define DBG_FRAMES()
+#endif
+
+#define DBG_MEM_START 1
+
+#if DBG_MEM
+
+#include <assert.h>
+extern int dbg_move_depth;
+#define MDBG_DUMP 1
+#define MDBG_OFFSET(a) ((int) ((uint8_t *) (a) - ao_lisp_pool))
+
+extern int dbg_mem;
+
+#define MDBG_DO(a) a
+#define MDBG_MOVE(...) do { if (dbg_mem) { int d; for (d = 0; d < dbg_move_depth; d++) printf (" "); printf(__VA_ARGS__); } } while (0)
+#define MDBG_MORE(...) do { if (dbg_mem) printf(__VA_ARGS__); } while (0)
+#define MDBG_MOVE_IN() (dbg_move_depth++)
+#define MDBG_MOVE_OUT() (assert(--dbg_move_depth >= 0))
+
+#else
+
+#define MDBG_DO(a)
+#define MDBG_MOVE(...)
+#define MDBG_MORE(...)
+#define MDBG_MOVE_IN()
+#define MDBG_MOVE_OUT()
+
+#endif
+
+#endif /* _AO_LISP_H_ */
diff --git a/src/lisp/ao_lisp_atom.c b/src/lisp/ao_lisp_atom.c
new file mode 100644
index 00000000..8c9e8ed1
--- /dev/null
+++ b/src/lisp/ao_lisp_atom.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_lisp.h"
+
+static int name_size(char *name)
+{
+ return sizeof(struct ao_lisp_atom) + strlen(name) + 1;
+}
+
+static int atom_size(void *addr)
+{
+ struct ao_lisp_atom *atom = addr;
+ if (!atom)
+ return 0;
+ return name_size(atom->name);
+}
+
+static void atom_mark(void *addr)
+{
+ struct ao_lisp_atom *atom = addr;
+
+ for (;;) {
+ atom = ao_lisp_poly_atom(atom->next);
+ if (!atom)
+ break;
+ if (ao_lisp_mark_memory(&ao_lisp_atom_type, atom))
+ break;
+ }
+}
+
+static void atom_move(void *addr)
+{
+ struct ao_lisp_atom *atom = addr;
+ int ret;
+
+ for (;;) {
+ struct ao_lisp_atom *next = ao_lisp_poly_atom(atom->next);
+
+ if (!next)
+ break;
+ ret = ao_lisp_move_memory(&ao_lisp_atom_type, (void **) &next);
+ if (next != ao_lisp_poly_atom(atom->next))
+ atom->next = ao_lisp_atom_poly(next);
+ if (ret)
+ break;
+ atom = next;
+ }
+}
+
+const struct ao_lisp_type ao_lisp_atom_type = {
+ .mark = atom_mark,
+ .size = atom_size,
+ .move = atom_move,
+ .name = "atom"
+};
+
+struct ao_lisp_atom *ao_lisp_atoms;
+
+struct ao_lisp_atom *
+ao_lisp_atom_intern(char *name)
+{
+ struct ao_lisp_atom *atom;
+
+ for (atom = ao_lisp_atoms; atom; atom = ao_lisp_poly_atom(atom->next)) {
+ if (!strcmp(atom->name, name))
+ return atom;
+ }
+#ifdef ao_builtin_atoms
+ for (atom = ao_lisp_poly_atom(ao_builtin_atoms); atom; atom = ao_lisp_poly_atom(atom->next)) {
+ if (!strcmp(atom->name, name))
+ return atom;
+ }
+#endif
+ ao_lisp_string_stash(0, name);
+ atom = ao_lisp_alloc(name_size(name));
+ name = ao_lisp_string_fetch(0);
+ if (atom) {
+ atom->type = AO_LISP_ATOM;
+ atom->next = ao_lisp_atom_poly(ao_lisp_atoms);
+ ao_lisp_atoms = atom;
+ strcpy(atom->name, name);
+ }
+ return atom;
+}
+
+struct ao_lisp_frame *ao_lisp_frame_global;
+struct ao_lisp_frame *ao_lisp_frame_current;
+
+static void
+ao_lisp_atom_init(void)
+{
+ if (!ao_lisp_frame_global)
+ ao_lisp_frame_global = ao_lisp_frame_new(0);
+}
+
+ao_poly *
+ao_lisp_atom_ref(struct ao_lisp_frame *frame, ao_poly atom)
+{
+ ao_poly *ref;
+ ao_lisp_atom_init();
+ while (frame) {
+ ref = ao_lisp_frame_ref(frame, atom);
+ if (ref)
+ return ref;
+ frame = ao_lisp_poly_frame(frame->prev);
+ }
+ if (ao_lisp_frame_global) {
+ ref = ao_lisp_frame_ref(ao_lisp_frame_global, atom);
+ if (ref)
+ return ref;
+ }
+ return NULL;
+}
+
+ao_poly
+ao_lisp_atom_get(ao_poly atom)
+{
+ ao_poly *ref = ao_lisp_atom_ref(ao_lisp_frame_current, atom);
+
+ if (!ref && ao_lisp_frame_global)
+ ref = ao_lisp_frame_ref(ao_lisp_frame_global, atom);
+#ifdef ao_builtin_frame
+ if (!ref)
+ ref = ao_lisp_frame_ref(ao_lisp_poly_frame(ao_builtin_frame), atom);
+#endif
+ if (ref)
+ return *ref;
+ return ao_lisp_error(AO_LISP_UNDEFINED, "undefined atom %s", ao_lisp_poly_atom(atom)->name);
+}
+
+ao_poly
+ao_lisp_atom_set(ao_poly atom, ao_poly val)
+{
+ ao_poly *ref = ao_lisp_atom_ref(ao_lisp_frame_current, atom);
+
+ if (!ref && ao_lisp_frame_global)
+ ref = ao_lisp_frame_ref(ao_lisp_frame_global, atom);
+ if (ref)
+ *ref = val;
+ else
+ ao_lisp_frame_add(&ao_lisp_frame_global, atom, val);
+ return val;
+}
+
+void
+ao_lisp_atom_print(ao_poly a)
+{
+ struct ao_lisp_atom *atom = ao_lisp_poly_atom(a);
+ printf("%s", atom->name);
+}
diff --git a/src/lisp/ao_lisp_builtin.c b/src/lisp/ao_lisp_builtin.c
new file mode 100644
index 00000000..902f60e2
--- /dev/null
+++ b/src/lisp/ao_lisp_builtin.c
@@ -0,0 +1,619 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+static int
+builtin_size(void *addr)
+{
+ (void) addr;
+ return sizeof (struct ao_lisp_builtin);
+}
+
+static void
+builtin_mark(void *addr)
+{
+ (void) addr;
+}
+
+static void
+builtin_move(void *addr)
+{
+ (void) addr;
+}
+
+const struct ao_lisp_type ao_lisp_builtin_type = {
+ .size = builtin_size,
+ .mark = builtin_mark,
+ .move = builtin_move
+};
+
+#ifdef AO_LISP_MAKE_CONST
+char *ao_lisp_builtin_name(enum ao_lisp_builtin_id b) {
+ (void) b;
+ return "???";
+}
+char *ao_lisp_args_name(uint8_t args) {
+ (void) args;
+ return "???";
+}
+#else
+static const ao_poly builtin_names[] = {
+ [builtin_eval] = _ao_lisp_atom_eval,
+ [builtin_read] = _ao_lisp_atom_read,
+ [builtin_lambda] = _ao_lisp_atom_lambda,
+ [builtin_lexpr] = _ao_lisp_atom_lexpr,
+ [builtin_nlambda] = _ao_lisp_atom_nlambda,
+ [builtin_macro] = _ao_lisp_atom_macro,
+ [builtin_car] = _ao_lisp_atom_car,
+ [builtin_cdr] = _ao_lisp_atom_cdr,
+ [builtin_cons] = _ao_lisp_atom_cons,
+ [builtin_last] = _ao_lisp_atom_last,
+ [builtin_length] = _ao_lisp_atom_length,
+ [builtin_quote] = _ao_lisp_atom_quote,
+ [builtin_set] = _ao_lisp_atom_set,
+ [builtin_setq] = _ao_lisp_atom_setq,
+ [builtin_cond] = _ao_lisp_atom_cond,
+ [builtin_progn] = _ao_lisp_atom_progn,
+ [builtin_while] = _ao_lisp_atom_while,
+ [builtin_print] = _ao_lisp_atom_print,
+ [builtin_patom] = _ao_lisp_atom_patom,
+ [builtin_plus] = _ao_lisp_atom_2b,
+ [builtin_minus] = _ao_lisp_atom_2d,
+ [builtin_times] = _ao_lisp_atom_2a,
+ [builtin_divide] = _ao_lisp_atom_2f,
+ [builtin_mod] = _ao_lisp_atom_25,
+ [builtin_equal] = _ao_lisp_atom_3d,
+ [builtin_less] = _ao_lisp_atom_3c,
+ [builtin_greater] = _ao_lisp_atom_3e,
+ [builtin_less_equal] = _ao_lisp_atom_3c3d,
+ [builtin_greater_equal] = _ao_lisp_atom_3e3d,
+ [builtin_pack] = _ao_lisp_atom_pack,
+ [builtin_unpack] = _ao_lisp_atom_unpack,
+ [builtin_flush] = _ao_lisp_atom_flush,
+ [builtin_delay] = _ao_lisp_atom_delay,
+ [builtin_led] = _ao_lisp_atom_led,
+ [builtin_save] = _ao_lisp_atom_save,
+ [builtin_restore] = _ao_lisp_atom_restore,
+ [builtin_call_cc] = _ao_lisp_atom_call2fcc,
+ [builtin_collect] = _ao_lisp_atom_collect,
+#if 0
+ [builtin_symbolp] = _ao_lisp_atom_symbolp,
+ [builtin_listp] = _ao_lisp_atom_listp,
+ [builtin_stringp] = _ao_lisp_atom_stringp,
+ [builtin_numberp] = _ao_lisp_atom_numberp,
+#endif
+};
+
+static char *
+ao_lisp_builtin_name(enum ao_lisp_builtin_id b) {
+ if (b < _builtin_last)
+ return ao_lisp_poly_atom(builtin_names[b])->name;
+ return "???";
+}
+
+static const ao_poly ao_lisp_args_atoms[] = {
+ [AO_LISP_FUNC_LAMBDA] = _ao_lisp_atom_lambda,
+ [AO_LISP_FUNC_LEXPR] = _ao_lisp_atom_lexpr,
+ [AO_LISP_FUNC_NLAMBDA] = _ao_lisp_atom_nlambda,
+ [AO_LISP_FUNC_MACRO] = _ao_lisp_atom_macro,
+};
+
+char *
+ao_lisp_args_name(uint8_t args)
+{
+ args &= AO_LISP_FUNC_MASK;
+ if (args < sizeof ao_lisp_args_atoms / sizeof ao_lisp_args_atoms[0])
+ return ao_lisp_poly_atom(ao_lisp_args_atoms[args])->name;
+ return "(unknown)";
+}
+#endif
+
+void
+ao_lisp_builtin_print(ao_poly b)
+{
+ struct ao_lisp_builtin *builtin = ao_lisp_poly_builtin(b);
+ printf("%s", ao_lisp_builtin_name(builtin->func));
+}
+
+ao_poly
+ao_lisp_check_argc(ao_poly name, struct ao_lisp_cons *cons, int min, int max)
+{
+ int argc = 0;
+
+ while (cons && argc <= max) {
+ argc++;
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ if (argc < min || argc > max)
+ return ao_lisp_error(AO_LISP_INVALID, "%s: invalid arg count", ao_lisp_poly_atom(name)->name);
+ return _ao_lisp_atom_t;
+}
+
+ao_poly
+ao_lisp_arg(struct ao_lisp_cons *cons, int argc)
+{
+ if (!cons)
+ return AO_LISP_NIL;
+ while (argc--) {
+ if (!cons)
+ return AO_LISP_NIL;
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ return cons->car;
+}
+
+ao_poly
+ao_lisp_check_argt(ao_poly name, struct ao_lisp_cons *cons, int argc, int type, int nil_ok)
+{
+ ao_poly car = ao_lisp_arg(cons, argc);
+
+ if ((!car && !nil_ok) || ao_lisp_poly_type(car) != type)
+ return ao_lisp_error(AO_LISP_INVALID, "%s: invalid type for arg %d", ao_lisp_poly_atom(name)->name, argc);
+ return _ao_lisp_atom_t;
+}
+
+ao_poly
+ao_lisp_car(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_car, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_car, cons, 0, AO_LISP_CONS, 0))
+ return AO_LISP_NIL;
+ return ao_lisp_poly_cons(cons->car)->car;
+}
+
+ao_poly
+ao_lisp_cdr(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_cdr, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_cdr, cons, 0, AO_LISP_CONS, 0))
+ return AO_LISP_NIL;
+ return ao_lisp_poly_cons(cons->car)->cdr;
+}
+
+ao_poly
+ao_lisp_cons(struct ao_lisp_cons *cons)
+{
+ ao_poly car, cdr;
+ if(!ao_lisp_check_argc(_ao_lisp_atom_cons, cons, 2, 2))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_cons, cons, 1, AO_LISP_CONS, 1))
+ return AO_LISP_NIL;
+ car = ao_lisp_arg(cons, 0);
+ cdr = ao_lisp_arg(cons, 1);
+ return ao_lisp_cons_poly(ao_lisp_cons_cons(car, ao_lisp_poly_cons(cdr)));
+}
+
+ao_poly
+ao_lisp_last(struct ao_lisp_cons *cons)
+{
+ ao_poly l;
+ if (!ao_lisp_check_argc(_ao_lisp_atom_last, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_last, cons, 0, AO_LISP_CONS, 1))
+ return AO_LISP_NIL;
+ l = ao_lisp_arg(cons, 0);
+ while (l) {
+ struct ao_lisp_cons *list = ao_lisp_poly_cons(l);
+ if (!list->cdr)
+ return list->car;
+ l = list->cdr;
+ }
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_lisp_length(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_length, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_length, cons, 0, AO_LISP_CONS, 1))
+ return AO_LISP_NIL;
+ return ao_lisp_int_poly(ao_lisp_cons_length(ao_lisp_poly_cons(ao_lisp_arg(cons, 0))));
+}
+
+ao_poly
+ao_lisp_quote(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_quote, cons, 1, 1))
+ return AO_LISP_NIL;
+ return ao_lisp_arg(cons, 0);
+}
+
+ao_poly
+ao_lisp_set(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_set, cons, 2, 2))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_set, cons, 0, AO_LISP_ATOM, 0))
+ return AO_LISP_NIL;
+
+ return ao_lisp_atom_set(ao_lisp_arg(cons, 0), ao_lisp_arg(cons, 1));
+}
+
+ao_poly
+ao_lisp_setq(struct ao_lisp_cons *cons)
+{
+ struct ao_lisp_cons *expand = 0;
+ if (!ao_lisp_check_argc(_ao_lisp_atom_setq, cons, 2, 2))
+ return AO_LISP_NIL;
+ expand = ao_lisp_cons_cons(_ao_lisp_atom_set,
+ ao_lisp_cons_cons(ao_lisp_cons_poly(ao_lisp_cons_cons(_ao_lisp_atom_quote,
+ ao_lisp_cons_cons(cons->car, NULL))),
+ ao_lisp_poly_cons(cons->cdr)));
+ return ao_lisp_cons_poly(expand);
+}
+
+ao_poly
+ao_lisp_cond(struct ao_lisp_cons *cons)
+{
+ ao_lisp_set_cond(cons);
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_lisp_progn(struct ao_lisp_cons *cons)
+{
+ ao_lisp_stack->state = eval_progn;
+ ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_lisp_while(struct ao_lisp_cons *cons)
+{
+ ao_lisp_stack->state = eval_while;
+ ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_lisp_print(struct ao_lisp_cons *cons)
+{
+ ao_poly val = AO_LISP_NIL;
+ while (cons) {
+ val = cons->car;
+ ao_lisp_poly_print(val);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ if (cons)
+ printf(" ");
+ }
+ printf("\n");
+ return val;
+}
+
+ao_poly
+ao_lisp_patom(struct ao_lisp_cons *cons)
+{
+ ao_poly val = AO_LISP_NIL;
+ while (cons) {
+ val = cons->car;
+ ao_lisp_poly_patom(val);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ return val;
+}
+
+ao_poly
+ao_lisp_math(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
+{
+ ao_poly ret = AO_LISP_NIL;
+
+ while (cons) {
+ ao_poly car = cons->car;
+ uint8_t rt = ao_lisp_poly_type(ret);
+ uint8_t ct = ao_lisp_poly_type(car);
+
+ cons = ao_lisp_poly_cons(cons->cdr);
+
+ if (rt == AO_LISP_NIL)
+ ret = car;
+
+ else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
+ int r = ao_lisp_poly_int(ret);
+ int c = ao_lisp_poly_int(car);
+
+ switch(op) {
+ case builtin_plus:
+ r += c;
+ break;
+ case builtin_minus:
+ r -= c;
+ break;
+ case builtin_times:
+ r *= c;
+ break;
+ case builtin_divide:
+ if (c == 0)
+ return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "divide by zero");
+ r /= c;
+ break;
+ case builtin_mod:
+ if (c == 0)
+ return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "mod by zero");
+ r %= c;
+ break;
+ default:
+ break;
+ }
+ ret = ao_lisp_int_poly(r);
+ }
+
+ else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == builtin_plus)
+ ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
+ ao_lisp_poly_string(car)));
+ else
+ return ao_lisp_error(AO_LISP_INVALID, "invalid args");
+ }
+ return ret;
+}
+
+ao_poly
+ao_lisp_plus(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_math(cons, builtin_plus);
+}
+
+ao_poly
+ao_lisp_minus(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_math(cons, builtin_minus);
+}
+
+ao_poly
+ao_lisp_times(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_math(cons, builtin_times);
+}
+
+ao_poly
+ao_lisp_divide(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_math(cons, builtin_divide);
+}
+
+ao_poly
+ao_lisp_mod(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_math(cons, builtin_mod);
+}
+
+ao_poly
+ao_lisp_compare(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
+{
+ ao_poly left;
+
+ if (!cons)
+ return _ao_lisp_atom_t;
+
+ left = cons->car;
+ cons = ao_lisp_poly_cons(cons->cdr);
+ while (cons) {
+ ao_poly right = cons->car;
+
+ if (op == builtin_equal) {
+ if (left != right)
+ return AO_LISP_NIL;
+ } else {
+ uint8_t lt = ao_lisp_poly_type(left);
+ uint8_t rt = ao_lisp_poly_type(right);
+ if (lt == AO_LISP_INT && rt == AO_LISP_INT) {
+ int l = ao_lisp_poly_int(left);
+ int r = ao_lisp_poly_int(right);
+
+ switch (op) {
+ case builtin_less:
+ if (!(l < r))
+ return AO_LISP_NIL;
+ break;
+ case builtin_greater:
+ if (!(l > r))
+ return AO_LISP_NIL;
+ break;
+ case builtin_less_equal:
+ if (!(l <= r))
+ return AO_LISP_NIL;
+ break;
+ case builtin_greater_equal:
+ if (!(l >= r))
+ return AO_LISP_NIL;
+ break;
+ default:
+ break;
+ }
+ } else if (lt == AO_LISP_STRING && rt == AO_LISP_STRING) {
+ int c = strcmp(ao_lisp_poly_string(left),
+ ao_lisp_poly_string(right));
+ switch (op) {
+ case builtin_less:
+ if (!(c < 0))
+ return AO_LISP_NIL;
+ break;
+ case builtin_greater:
+ if (!(c > 0))
+ return AO_LISP_NIL;
+ break;
+ case builtin_less_equal:
+ if (!(c <= 0))
+ return AO_LISP_NIL;
+ break;
+ case builtin_greater_equal:
+ if (!(c >= 0))
+ return AO_LISP_NIL;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ left = right;
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ return _ao_lisp_atom_t;
+}
+
+ao_poly
+ao_lisp_equal(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_compare(cons, builtin_equal);
+}
+
+ao_poly
+ao_lisp_less(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_compare(cons, builtin_less);
+}
+
+ao_poly
+ao_lisp_greater(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_compare(cons, builtin_greater);
+}
+
+ao_poly
+ao_lisp_less_equal(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_compare(cons, builtin_less_equal);
+}
+
+ao_poly
+ao_lisp_greater_equal(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_compare(cons, builtin_greater_equal);
+}
+
+ao_poly
+ao_lisp_pack(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_pack, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_pack, cons, 0, AO_LISP_CONS, 1))
+ return AO_LISP_NIL;
+ return ao_lisp_string_pack(ao_lisp_poly_cons(ao_lisp_arg(cons, 0)));
+}
+
+ao_poly
+ao_lisp_unpack(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_unpack, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_unpack, cons, 0, AO_LISP_STRING, 0))
+ return AO_LISP_NIL;
+ return ao_lisp_string_unpack(ao_lisp_poly_string(ao_lisp_arg(cons, 0)));
+}
+
+ao_poly
+ao_lisp_flush(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_flush, cons, 0, 0))
+ return AO_LISP_NIL;
+ ao_lisp_os_flush();
+ return _ao_lisp_atom_t;
+}
+
+ao_poly
+ao_lisp_led(struct ao_lisp_cons *cons)
+{
+ ao_poly led;
+ if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
+ return AO_LISP_NIL;
+ led = ao_lisp_arg(cons, 0);
+ ao_lisp_os_led(ao_lisp_poly_int(led));
+ return led;
+}
+
+ao_poly
+ao_lisp_delay(struct ao_lisp_cons *cons)
+{
+ ao_poly delay;
+ if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
+ return AO_LISP_NIL;
+ delay = ao_lisp_arg(cons, 0);
+ ao_lisp_os_delay(ao_lisp_poly_int(delay));
+ return delay;
+}
+
+ao_poly
+ao_lisp_do_eval(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_eval, cons, 1, 1))
+ return AO_LISP_NIL;
+ ao_lisp_stack->state = eval_sexpr;
+ return cons->car;
+}
+
+ao_poly
+ao_lisp_do_read(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_read, cons, 0, 0))
+ return AO_LISP_NIL;
+ return ao_lisp_read();
+}
+
+ao_poly
+ao_lisp_do_collect(struct ao_lisp_cons *cons)
+{
+ int free;
+ (void) cons;
+ free = ao_lisp_collect(AO_LISP_COLLECT_FULL);
+ return ao_lisp_int_poly(free);
+}
+
+const ao_lisp_func_t ao_lisp_builtins[] = {
+ [builtin_eval] = ao_lisp_do_eval,
+ [builtin_read] = ao_lisp_do_read,
+ [builtin_lambda] = ao_lisp_lambda,
+ [builtin_lexpr] = ao_lisp_lexpr,
+ [builtin_nlambda] = ao_lisp_nlambda,
+ [builtin_macro] = ao_lisp_macro,
+ [builtin_car] = ao_lisp_car,
+ [builtin_cdr] = ao_lisp_cdr,
+ [builtin_cons] = ao_lisp_cons,
+ [builtin_last] = ao_lisp_last,
+ [builtin_length] = ao_lisp_length,
+ [builtin_quote] = ao_lisp_quote,
+ [builtin_set] = ao_lisp_set,
+ [builtin_setq] = ao_lisp_setq,
+ [builtin_cond] = ao_lisp_cond,
+ [builtin_progn] = ao_lisp_progn,
+ [builtin_while] = ao_lisp_while,
+ [builtin_print] = ao_lisp_print,
+ [builtin_patom] = ao_lisp_patom,
+ [builtin_plus] = ao_lisp_plus,
+ [builtin_minus] = ao_lisp_minus,
+ [builtin_times] = ao_lisp_times,
+ [builtin_divide] = ao_lisp_divide,
+ [builtin_mod] = ao_lisp_mod,
+ [builtin_equal] = ao_lisp_equal,
+ [builtin_less] = ao_lisp_less,
+ [builtin_greater] = ao_lisp_greater,
+ [builtin_less_equal] = ao_lisp_less_equal,
+ [builtin_greater_equal] = ao_lisp_greater_equal,
+ [builtin_pack] = ao_lisp_pack,
+ [builtin_unpack] = ao_lisp_unpack,
+ [builtin_flush] = ao_lisp_flush,
+ [builtin_led] = ao_lisp_led,
+ [builtin_delay] = ao_lisp_delay,
+ [builtin_save] = ao_lisp_save,
+ [builtin_restore] = ao_lisp_restore,
+ [builtin_call_cc] = ao_lisp_call_cc,
+ [builtin_collect] = ao_lisp_do_collect,
+};
+
diff --git a/src/lisp/ao_lisp_cons.c b/src/lisp/ao_lisp_cons.c
new file mode 100644
index 00000000..d2b60c9a
--- /dev/null
+++ b/src/lisp/ao_lisp_cons.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+static void cons_mark(void *addr)
+{
+ struct ao_lisp_cons *cons = addr;
+
+ for (;;) {
+ ao_lisp_poly_mark(cons->car, 1);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ if (!cons)
+ break;
+ if (ao_lisp_mark_memory(&ao_lisp_cons_type, cons))
+ break;
+ }
+}
+
+static int cons_size(void *addr)
+{
+ (void) addr;
+ return sizeof (struct ao_lisp_cons);
+}
+
+static void cons_move(void *addr)
+{
+ struct ao_lisp_cons *cons = addr;
+
+ if (!cons)
+ return;
+
+ for (;;) {
+ struct ao_lisp_cons *cdr;
+ int ret;
+
+ MDBG_MOVE("cons_move start %d (%d, %d)\n",
+ MDBG_OFFSET(cons), MDBG_OFFSET(ao_lisp_ref(cons->car)), MDBG_OFFSET(ao_lisp_ref(cons->cdr)));
+ (void) ao_lisp_poly_move(&cons->car, 1);
+ cdr = ao_lisp_poly_cons(cons->cdr);
+ if (!cdr)
+ break;
+ ret = ao_lisp_move_memory(&ao_lisp_cons_type, (void **) &cdr);
+ if (cdr != ao_lisp_poly_cons(cons->cdr))
+ cons->cdr = ao_lisp_cons_poly(cdr);
+ MDBG_MOVE("cons_move end %d (%d, %d)\n",
+ MDBG_OFFSET(cons), MDBG_OFFSET(ao_lisp_ref(cons->car)), MDBG_OFFSET(ao_lisp_ref(cons->cdr)));
+ if (ret)
+ break;
+ cons = cdr;
+ }
+}
+
+const struct ao_lisp_type ao_lisp_cons_type = {
+ .mark = cons_mark,
+ .size = cons_size,
+ .move = cons_move,
+ .name = "cons",
+};
+
+struct ao_lisp_cons *ao_lisp_cons_free_list;
+
+struct ao_lisp_cons *
+ao_lisp_cons_cons(ao_poly car, struct ao_lisp_cons *cdr)
+{
+ struct ao_lisp_cons *cons;
+
+ if (ao_lisp_cons_free_list) {
+ cons = ao_lisp_cons_free_list;
+ ao_lisp_cons_free_list = ao_lisp_poly_cons(cons->cdr);
+ } else {
+ ao_lisp_poly_stash(0, car);
+ ao_lisp_cons_stash(0, cdr);
+ cons = ao_lisp_alloc(sizeof (struct ao_lisp_cons));
+ car = ao_lisp_poly_fetch(0);
+ cdr = ao_lisp_cons_fetch(0);
+ if (!cons)
+ return NULL;
+ }
+ cons->car = car;
+ cons->cdr = ao_lisp_cons_poly(cdr);
+ return cons;
+}
+
+void
+ao_lisp_cons_free(struct ao_lisp_cons *cons)
+{
+ while (cons) {
+ ao_poly cdr = cons->cdr;
+ cons->cdr = ao_lisp_cons_poly(ao_lisp_cons_free_list);
+ ao_lisp_cons_free_list = cons;
+ cons = ao_lisp_poly_cons(cdr);
+ }
+}
+
+void
+ao_lisp_cons_print(ao_poly c)
+{
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(c);
+ int first = 1;
+ printf("(");
+ while (cons) {
+ if (!first)
+ printf(" ");
+ ao_lisp_poly_print(cons->car);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ first = 0;
+ }
+ printf(")");
+}
+
+void
+ao_lisp_cons_patom(ao_poly c)
+{
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(c);
+
+ while (cons) {
+ ao_lisp_poly_patom(cons->car);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+}
+
+int
+ao_lisp_cons_length(struct ao_lisp_cons *cons)
+{
+ int len = 0;
+ while (cons) {
+ len++;
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ return len;
+}
diff --git a/src/lisp/ao_lisp_const.lisp b/src/lisp/ao_lisp_const.lisp
new file mode 100644
index 00000000..3c8fd21b
--- /dev/null
+++ b/src/lisp/ao_lisp_const.lisp
@@ -0,0 +1,184 @@
+;
+; Copyright © 2016 Keith Packard <keithp@keithp.com>
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; Lisp code placed in ROM
+
+ ; return a list containing all of the arguments
+
+(set (quote list) (lexpr (l) l))
+
+ ;
+ ; Define a variable without returning the value
+ ; Useful when defining functions to avoid
+ ; having lots of output generated
+ ;
+
+(setq def (macro (name val rest)
+ (list
+ 'progn
+ (list
+ 'set
+ (list 'quote name)
+ val)
+ (list 'quote name)
+ )
+ )
+ )
+
+ ;
+ ; A slightly more convenient form
+ ; for defining lambdas.
+ ;
+ ; (defun <name> (<params>) s-exprs)
+ ;
+
+(def defun (macro (name args exprs)
+ (list
+ def
+ name
+ (cons 'lambda (cons args exprs))
+ )
+ )
+ )
+
+ ; basic list accessors
+
+
+(defun cadr (l) (car (cdr l)))
+
+(defun caddr (l) (car (cdr (cdr l))))
+
+(defun nth (list n)
+ (cond ((= n 0) (car list))
+ ((nth (cdr list) (1- n)))
+ )
+ )
+
+ ; simple math operators
+
+(defun 1+ (x) (+ x 1))
+(defun 1- (x) (- x 1))
+
+ ; define a set of local
+ ; variables and then evaluate
+ ; a list of sexprs
+ ;
+ ; (let (var-defines) sexprs)
+ ;
+ ; where var-defines are either
+ ;
+ ; (name value)
+ ;
+ ; or
+ ;
+ ; (name)
+ ;
+ ; e.g.
+ ;
+ ; (let ((x 1) (y)) (setq y (+ x 1)) y)
+
+(def let (macro (vars exprs)
+ ((lambda (make-names make-exprs make-nils)
+
+ ;
+ ; make the list of names in the let
+ ;
+
+ (setq make-names (lambda (vars)
+ (cond (vars
+ (cons (car (car vars))
+ (make-names (cdr vars))))
+ )
+ )
+ )
+
+ ; the set of expressions is
+ ; the list of set expressions
+ ; pre-pended to the
+ ; expressions to evaluate
+
+ (setq make-exprs (lambda (vars exprs)
+ (cond (vars (cons
+ (list set
+ (list quote
+ (car (car vars))
+ )
+ (cadr (car vars))
+ )
+ (make-exprs (cdr vars) exprs)
+ )
+ )
+ (exprs)
+ )
+ )
+ )
+
+ ; the parameters to the lambda is a list
+ ; of nils of the right length
+
+ (setq make-nils (lambda (vars)
+ (cond (vars (cons nil (make-nils (cdr vars))))
+ )
+ )
+ )
+ ; prepend the set operations
+ ; to the expressions
+
+ (setq exprs (make-exprs vars exprs))
+
+ ; build the lambda.
+
+ (cons (cons 'lambda (cons (make-names vars) exprs))
+ (make-nils vars)
+ )
+ )
+ ()
+ ()
+ ()
+ )
+ )
+ )
+
+ ; boolean operators
+
+(def or (lexpr (l)
+ (let ((ret nil))
+ (while l
+ (cond ((setq ret (car l))
+ (setq l nil))
+ ((setq l (cdr l)))))
+ ret
+ )
+ )
+ )
+
+ ; execute to resolve macros
+
+(or nil t)
+
+(def and (lexpr (l)
+ (let ((ret t))
+ (while l
+ (cond ((setq ret (car l))
+ (setq l (cdr l)))
+ ((setq ret (setq l nil)))
+ )
+ )
+ ret
+ )
+ )
+ )
+
+ ; execute to resolve macros
+
+(and t nil)
diff --git a/src/lisp/ao_lisp_error.c b/src/lisp/ao_lisp_error.c
new file mode 100644
index 00000000..54a9be10
--- /dev/null
+++ b/src/lisp/ao_lisp_error.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+#include <stdarg.h>
+
+void
+ao_lisp_error_poly(char *name, ao_poly poly, ao_poly last)
+{
+ int first = 1;
+ printf("\t\t%s(", name);
+ if (ao_lisp_poly_type(poly) == AO_LISP_CONS) {
+ if (poly) {
+ while (poly) {
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(poly);
+ if (!first)
+ printf("\t\t ");
+ else
+ first = 0;
+ ao_lisp_poly_print(cons->car);
+ printf("\n");
+ if (poly == last)
+ break;
+ poly = cons->cdr;
+ }
+ printf("\t\t )\n");
+ } else
+ printf(")\n");
+ } else {
+ ao_lisp_poly_print(poly);
+ printf("\n");
+ }
+}
+
+static void tabs(int indent)
+{
+ while (indent--)
+ printf("\t");
+}
+
+void
+ao_lisp_error_frame(int indent, char *name, struct ao_lisp_frame *frame)
+{
+ int f;
+
+ tabs(indent);
+ printf ("%s{", name);
+ if (frame) {
+ if (frame->type & AO_LISP_FRAME_PRINT)
+ printf("recurse...");
+ else {
+ frame->type |= AO_LISP_FRAME_PRINT;
+ for (f = 0; f < frame->num; f++) {
+ if (f != 0) {
+ tabs(indent);
+ printf(" ");
+ }
+ ao_lisp_poly_print(frame->vals[f].atom);
+ printf(" = ");
+ ao_lisp_poly_print(frame->vals[f].val);
+ printf("\n");
+ }
+ if (frame->prev)
+ ao_lisp_error_frame(indent + 1, "prev: ", ao_lisp_poly_frame(frame->prev));
+ frame->type &= ~AO_LISP_FRAME_PRINT;
+ }
+ tabs(indent);
+ printf(" }\n");
+ } else
+ printf ("}\n");
+}
+
+
+ao_poly
+ao_lisp_error(int error, char *format, ...)
+{
+ va_list args;
+
+ ao_lisp_exception |= error;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ printf("Value: "); ao_lisp_poly_print(ao_lisp_v); printf("\n");
+ printf("Stack:\n");
+ ao_lisp_stack_print(ao_lisp_stack_poly(ao_lisp_stack));
+ printf("Globals:\n\t");
+ ao_lisp_frame_print(ao_lisp_frame_poly(ao_lisp_frame_global));
+ printf("\n");
+ return AO_LISP_NIL;
+}
diff --git a/src/lisp/ao_lisp_eval.c b/src/lisp/ao_lisp_eval.c
new file mode 100644
index 00000000..3be7c9c4
--- /dev/null
+++ b/src/lisp/ao_lisp_eval.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+#include <assert.h>
+
+struct ao_lisp_stack *ao_lisp_stack;
+ao_poly ao_lisp_v;
+
+ao_poly
+ao_lisp_set_cond(struct ao_lisp_cons *c)
+{
+ ao_lisp_stack->state = eval_cond;
+ ao_lisp_stack->sexprs = ao_lisp_cons_poly(c);
+ return AO_LISP_NIL;
+}
+
+static int
+func_type(ao_poly func)
+{
+ if (func == AO_LISP_NIL)
+ return ao_lisp_error(AO_LISP_INVALID, "func is nil");
+ switch (ao_lisp_poly_type(func)) {
+ case AO_LISP_BUILTIN:
+ return ao_lisp_poly_builtin(func)->args & AO_LISP_FUNC_MASK;
+ case AO_LISP_LAMBDA:
+ return ao_lisp_poly_lambda(func)->args;
+ case AO_LISP_STACK:
+ return AO_LISP_FUNC_LAMBDA;
+ default:
+ ao_lisp_error(AO_LISP_INVALID, "not a func");
+ return -1;
+ }
+}
+
+/*
+ * Flattened eval to avoid stack issues
+ */
+
+/*
+ * Evaluate an s-expression
+ *
+ * For a list, evaluate all of the elements and
+ * then execute the resulting function call.
+ *
+ * Each element of the list is evaluated in
+ * a clean stack context.
+ *
+ * The current stack state is set to 'formal' so that
+ * when the evaluation is complete, the value
+ * will get appended to the values list.
+ *
+ * For other types, compute the value directly.
+ */
+
+static int
+ao_lisp_eval_sexpr(void)
+{
+ DBGI("sexpr: "); DBG_POLY(ao_lisp_v); DBG("\n");
+ switch (ao_lisp_poly_type(ao_lisp_v)) {
+ case AO_LISP_CONS:
+ if (ao_lisp_v == AO_LISP_NIL) {
+ if (!ao_lisp_stack->values) {
+ /*
+ * empty list evaluates to empty list
+ */
+ ao_lisp_v = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_val;
+ } else {
+ /*
+ * done with arguments, go execute it
+ */
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->values)->car;
+ ao_lisp_stack->state = eval_exec;
+ }
+ } else {
+ if (!ao_lisp_stack->values)
+ ao_lisp_stack->list = ao_lisp_v;
+ /*
+ * Evaluate another argument and then switch
+ * to 'formal' to add the value to the values
+ * list
+ */
+ ao_lisp_stack->sexprs = ao_lisp_v;
+ ao_lisp_stack->state = eval_formal;
+ if (!ao_lisp_stack_push())
+ return 0;
+ /*
+ * push will reset the state to 'sexpr', which
+ * will evaluate the expression
+ */
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_v)->car;
+ }
+ break;
+ case AO_LISP_ATOM:
+ DBGI("..frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ ao_lisp_v = ao_lisp_atom_get(ao_lisp_v);
+ /* fall through */
+ case AO_LISP_INT:
+ case AO_LISP_STRING:
+ case AO_LISP_BUILTIN:
+ case AO_LISP_LAMBDA:
+ ao_lisp_stack->state = eval_val;
+ break;
+ }
+ DBGI(".. result "); DBG_POLY(ao_lisp_v); DBG("\n");
+ return 1;
+}
+
+/*
+ * A value has been computed.
+ *
+ * If the value was computed from a macro,
+ * then we want to reset the current context
+ * to evaluate the macro result again.
+ *
+ * If not a macro, then pop the stack.
+ * If the stack is empty, we're done.
+ * Otherwise, the stack will contain
+ * the next state.
+ */
+
+static int
+ao_lisp_eval_val(void)
+{
+ DBGI("val: "); DBG_POLY(ao_lisp_v); DBG("\n");
+ /*
+ * Value computed, pop the stack
+ * to figure out what to do with the value
+ */
+ ao_lisp_stack_pop();
+ DBGI("..state %d\n", ao_lisp_stack ? ao_lisp_stack->state : -1);
+ return 1;
+}
+
+/*
+ * A formal has been computed.
+ *
+ * If this is the first formal, then check to see if we've got a
+ * lamda/lexpr or macro/nlambda.
+ *
+ * For lambda/lexpr, go compute another formal. This will terminate
+ * when the sexpr state sees nil.
+ *
+ * For macro/nlambda, we're done, so move the sexprs into the values
+ * and go execute it.
+ *
+ * Macros have an additional step of saving a stack frame holding the
+ * macro value execution context, which then gets the result of the
+ * macro to run
+ */
+
+static int
+ao_lisp_eval_formal(void)
+{
+ ao_poly formal;
+ struct ao_lisp_stack *prev;
+
+ DBGI("formal: "); DBG_POLY(ao_lisp_v); DBG("\n");
+
+ /* Check what kind of function we've got */
+ if (!ao_lisp_stack->values) {
+ switch (func_type(ao_lisp_v)) {
+ case AO_LISP_FUNC_LAMBDA:
+ case AO_LISP_FUNC_LEXPR:
+ DBGI(".. lambda or lexpr\n");
+ break;
+ case AO_LISP_FUNC_MACRO:
+ /* Evaluate the result once more */
+ ao_lisp_stack->state = eval_macro;
+ if (!ao_lisp_stack_push())
+ return 0;
+
+ /* After the function returns, take that
+ * value and re-evaluate it
+ */
+ prev = ao_lisp_poly_stack(ao_lisp_stack->prev);
+ ao_lisp_stack->sexprs = prev->sexprs;
+
+ DBGI(".. start macro\n");
+ DBGI(".. sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. values "); DBG_POLY(ao_lisp_stack->values); DBG("\n");
+ DBG_FRAMES();
+
+ /* fall through ... */
+ case AO_LISP_FUNC_NLAMBDA:
+ DBGI(".. nlambda or macro\n");
+
+ /* use the raw sexprs as values */
+ ao_lisp_stack->values = ao_lisp_stack->sexprs;
+ ao_lisp_stack->values_tail = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_exec;
+
+ /* ready to execute now */
+ return 1;
+ case -1:
+ return 0;
+ }
+ }
+
+ /* Append formal to list of values */
+ formal = ao_lisp_cons_poly(ao_lisp_cons_cons(ao_lisp_v, NULL));
+ if (!formal)
+ return 0;
+
+ if (ao_lisp_stack->values_tail)
+ ao_lisp_poly_cons(ao_lisp_stack->values_tail)->cdr = formal;
+ else
+ ao_lisp_stack->values = formal;
+ ao_lisp_stack->values_tail = formal;
+
+ DBGI(".. values "); DBG_POLY(ao_lisp_stack->values); DBG("\n");
+
+ /*
+ * Step to the next argument, if this is last, then
+ * 'sexpr' will end up switching to 'exec'
+ */
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+
+ ao_lisp_stack->state = eval_sexpr;
+
+ DBGI(".. "); DBG_POLY(ao_lisp_v); DBG("\n");
+ return 1;
+}
+
+/*
+ * Start executing a function call
+ *
+ * Most builtins are easy, just call the function.
+ * 'cond' is magic; it sticks the list of clauses
+ * in 'sexprs' and switches to 'cond' state. That
+ * bit of magic is done in ao_lisp_set_cond.
+ *
+ * Lambdas build a new frame to hold the locals and
+ * then re-use the current stack context to evaluate
+ * the s-expression from the lambda.
+ */
+
+static int
+ao_lisp_eval_exec(void)
+{
+ ao_poly v;
+ struct ao_lisp_builtin *builtin;
+
+ DBGI("exec: "); DBG_POLY(ao_lisp_v); DBG(" values "); DBG_POLY(ao_lisp_stack->values); DBG ("\n");
+ ao_lisp_stack->sexprs = AO_LISP_NIL;
+ switch (ao_lisp_poly_type(ao_lisp_v)) {
+ case AO_LISP_BUILTIN:
+ ao_lisp_stack->state = eval_val;
+ builtin = ao_lisp_poly_builtin(ao_lisp_v);
+ v = ao_lisp_func(builtin) (
+ ao_lisp_poly_cons(ao_lisp_poly_cons(ao_lisp_stack->values)->cdr));
+ DBG_DO(if (!ao_lisp_exception && ao_lisp_poly_builtin(ao_lisp_v)->func == builtin_set) {
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(ao_lisp_stack->values);
+ ao_poly atom = ao_lisp_arg(cons, 1);
+ ao_poly val = ao_lisp_arg(cons, 2);
+ DBGI("set "); DBG_POLY(atom); DBG(" = "); DBG_POLY(val); DBG("\n");
+ });
+ builtin = ao_lisp_poly_builtin(ao_lisp_v);
+ if (builtin->args & AO_LISP_FUNC_FREE_ARGS && !ao_lisp_stack_marked(ao_lisp_stack))
+ ao_lisp_cons_free(ao_lisp_poly_cons(ao_lisp_stack->values));
+
+ ao_lisp_v = v;
+ ao_lisp_stack->values = AO_LISP_NIL;
+ ao_lisp_stack->values_tail = AO_LISP_NIL;
+ DBGI(".. result "); DBG_POLY(ao_lisp_v); DBG ("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ break;
+ case AO_LISP_LAMBDA:
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ ao_lisp_stack->state = eval_progn;
+ v = ao_lisp_lambda_eval();
+ ao_lisp_stack->sexprs = v;
+ ao_lisp_stack->values = AO_LISP_NIL;
+ ao_lisp_stack->values_tail = AO_LISP_NIL;
+ DBGI(".. sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ break;
+ case AO_LISP_STACK:
+ DBGI(".. stack "); DBG_POLY(ao_lisp_v); DBG("\n");
+ ao_lisp_v = ao_lisp_stack_eval();
+ DBGI(".. value "); DBG_POLY(ao_lisp_v); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ break;
+ }
+ return 1;
+}
+
+/*
+ * Start evaluating the next cond clause
+ *
+ * If the list of clauses is empty, then
+ * the result of the cond is nil.
+ *
+ * Otherwise, set the current stack state to 'cond_test' and create a
+ * new stack context to evaluate the test s-expression. Once that's
+ * complete, we'll land in 'cond_test' to finish the clause.
+ */
+static int
+ao_lisp_eval_cond(void)
+{
+ DBGI("cond: "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+ if (!ao_lisp_stack->sexprs) {
+ ao_lisp_v = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_val;
+ } else {
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car;
+ if (!ao_lisp_v || ao_lisp_poly_type(ao_lisp_v) != AO_LISP_CONS) {
+ ao_lisp_error(AO_LISP_INVALID, "invalid cond clause");
+ return 0;
+ }
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_v)->car;
+ ao_lisp_stack->state = eval_cond_test;
+ if (!ao_lisp_stack_push())
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Finish a cond clause.
+ *
+ * Check the value from the test expression, if
+ * non-nil, then set up to evaluate the value expression.
+ *
+ * Otherwise, step to the next clause and go back to the 'cond'
+ * state
+ */
+static int
+ao_lisp_eval_cond_test(void)
+{
+ DBGI("cond_test: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+ if (ao_lisp_v) {
+ struct ao_lisp_cons *car = ao_lisp_poly_cons(ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car);
+ ao_poly c = car->cdr;
+
+ if (c) {
+ ao_lisp_stack->state = eval_progn;
+ ao_lisp_stack->sexprs = c;
+ } else
+ ao_lisp_stack->state = eval_val;
+ } else {
+ ao_lisp_stack->sexprs = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+ DBGI("next cond: "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ ao_lisp_stack->state = eval_cond;
+ }
+ return 1;
+}
+
+/*
+ * Evaluate a list of sexprs, returning the value from the last one.
+ *
+ * ao_lisp_progn records the list in stack->sexprs, so we just need to
+ * walk that list. Set ao_lisp_v to the car of the list and jump to
+ * eval_sexpr. When that's done, it will land in eval_val. For all but
+ * the last, leave a stack frame with eval_progn set so that we come
+ * back here. For the last, don't add a stack frame so that we can
+ * just continue on.
+ */
+static int
+ao_lisp_eval_progn(void)
+{
+ DBGI("progn: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+ if (!ao_lisp_stack->sexprs) {
+ ao_lisp_v = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_val;
+ } else {
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car;
+ ao_lisp_stack->sexprs = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+
+ /* If there are more sexprs to do, then come back here, otherwise
+ * return the value of the last one by just landing in eval_sexpr
+ */
+ if (ao_lisp_stack->sexprs) {
+ ao_lisp_stack->state = eval_progn;
+ if (!ao_lisp_stack_push())
+ return 0;
+ }
+ ao_lisp_stack->state = eval_sexpr;
+ }
+ return 1;
+}
+
+/*
+ * Conditionally execute a list of sexprs while the first is true
+ */
+static int
+ao_lisp_eval_while(void)
+{
+ DBGI("while: "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+ ao_lisp_stack->values = ao_lisp_v;
+ if (!ao_lisp_stack->sexprs) {
+ ao_lisp_v = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_val;
+ } else {
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car;
+ ao_lisp_stack->state = eval_while_test;
+ if (!ao_lisp_stack_push())
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Check the while condition, terminate the loop if nil. Otherwise keep going
+ */
+static int
+ao_lisp_eval_while_test(void)
+{
+ DBGI("while_test: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+ DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+ DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+ if (ao_lisp_v) {
+ ao_lisp_stack->values = ao_lisp_v;
+ ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+ ao_lisp_stack->state = eval_while;
+ if (!ao_lisp_stack_push())
+ return 0;
+ ao_lisp_stack->state = eval_progn;
+ ao_lisp_stack->sexprs = ao_lisp_v;
+ }
+ else
+ {
+ ao_lisp_stack->state = eval_val;
+ ao_lisp_v = ao_lisp_stack->values;
+ }
+ return 1;
+}
+
+/*
+ * Replace the original sexpr with the macro expansion, then
+ * execute that
+ */
+static int
+ao_lisp_eval_macro(void)
+{
+ DBGI("macro: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+
+ if (ao_lisp_v == AO_LISP_NIL)
+ ao_lisp_abort();
+ if (ao_lisp_poly_type(ao_lisp_v) == AO_LISP_CONS) {
+ *ao_lisp_poly_cons(ao_lisp_stack->sexprs) = *ao_lisp_poly_cons(ao_lisp_v);
+ ao_lisp_v = ao_lisp_stack->sexprs;
+ DBGI("sexprs rewritten to: "); DBG_POLY(ao_lisp_v); DBG("\n");
+ }
+ ao_lisp_stack->sexprs = AO_LISP_NIL;
+ ao_lisp_stack->state = eval_sexpr;
+ return 1;
+}
+
+static int (*const evals[])(void) = {
+ [eval_sexpr] = ao_lisp_eval_sexpr,
+ [eval_val] = ao_lisp_eval_val,
+ [eval_formal] = ao_lisp_eval_formal,
+ [eval_exec] = ao_lisp_eval_exec,
+ [eval_cond] = ao_lisp_eval_cond,
+ [eval_cond_test] = ao_lisp_eval_cond_test,
+ [eval_progn] = ao_lisp_eval_progn,
+ [eval_while] = ao_lisp_eval_while,
+ [eval_while_test] = ao_lisp_eval_while_test,
+ [eval_macro] = ao_lisp_eval_macro,
+};
+
+const char *ao_lisp_state_names[] = {
+ "sexpr",
+ "val",
+ "formal",
+ "exec",
+ "cond",
+ "cond_test",
+ "progn",
+};
+
+/*
+ * Called at restore time to reset all execution state
+ */
+
+void
+ao_lisp_eval_clear_globals(void)
+{
+ ao_lisp_stack = NULL;
+ ao_lisp_frame_current = NULL;
+ ao_lisp_v = AO_LISP_NIL;
+}
+
+int
+ao_lisp_eval_restart(void)
+{
+ return ao_lisp_stack_push();
+}
+
+ao_poly
+ao_lisp_eval(ao_poly _v)
+{
+ ao_lisp_v = _v;
+
+ if (!ao_lisp_stack_push())
+ return AO_LISP_NIL;
+
+ while (ao_lisp_stack) {
+ if (!(*evals[ao_lisp_stack->state])() || ao_lisp_exception) {
+ ao_lisp_stack_clear();
+ return AO_LISP_NIL;
+ }
+ }
+ DBG_DO(if (ao_lisp_frame_current) {DBGI("frame left as "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");});
+ ao_lisp_frame_current = NULL;
+ return ao_lisp_v;
+}
diff --git a/src/lisp/ao_lisp_frame.c b/src/lisp/ao_lisp_frame.c
new file mode 100644
index 00000000..05f6d253
--- /dev/null
+++ b/src/lisp/ao_lisp_frame.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+static inline int
+frame_num_size(int num)
+{
+ return sizeof (struct ao_lisp_frame) + num * sizeof (struct ao_lisp_val);
+}
+
+static int
+frame_size(void *addr)
+{
+ struct ao_lisp_frame *frame = addr;
+ return frame_num_size(frame->num);
+}
+
+static void
+frame_mark(void *addr)
+{
+ struct ao_lisp_frame *frame = addr;
+ int f;
+
+ for (;;) {
+ MDBG_MOVE("frame mark %d\n", MDBG_OFFSET(frame));
+ if (!AO_LISP_IS_POOL(frame))
+ break;
+ for (f = 0; f < frame->num; f++) {
+ struct ao_lisp_val *v = &frame->vals[f];
+
+ ao_lisp_poly_mark(v->val, 0);
+ MDBG_MOVE("frame mark atom %s %d val %d at %d\n",
+ ao_lisp_poly_atom(v->atom)->name,
+ MDBG_OFFSET(ao_lisp_ref(v->atom)),
+ MDBG_OFFSET(ao_lisp_ref(v->val)), f);
+ }
+ frame = ao_lisp_poly_frame(frame->prev);
+ MDBG_MOVE("frame next %d\n", MDBG_OFFSET(frame));
+ if (!frame)
+ break;
+ if (ao_lisp_mark_memory(&ao_lisp_frame_type, frame))
+ break;
+ }
+}
+
+static void
+frame_move(void *addr)
+{
+ struct ao_lisp_frame *frame = addr;
+ int f;
+
+ for (;;) {
+ struct ao_lisp_frame *prev;
+ int ret;
+
+ MDBG_MOVE("frame move %d\n", MDBG_OFFSET(frame));
+ if (!AO_LISP_IS_POOL(frame))
+ break;
+ for (f = 0; f < frame->num; f++) {
+ struct ao_lisp_val *v = &frame->vals[f];
+
+ ao_lisp_poly_move(&v->atom, 0);
+ ao_lisp_poly_move(&v->val, 0);
+ MDBG_MOVE("frame move atom %s %d val %d at %d\n",
+ ao_lisp_poly_atom(v->atom)->name,
+ MDBG_OFFSET(ao_lisp_ref(v->atom)),
+ MDBG_OFFSET(ao_lisp_ref(v->val)), f);
+ }
+ prev = ao_lisp_poly_frame(frame->prev);
+ if (!prev)
+ break;
+ ret = ao_lisp_move_memory(&ao_lisp_frame_type, (void **) &prev);
+ if (prev != ao_lisp_poly_frame(frame->prev)) {
+ MDBG_MOVE("frame prev moved from %d to %d\n",
+ MDBG_OFFSET(ao_lisp_poly_frame(frame->prev)),
+ MDBG_OFFSET(prev));
+ frame->prev = ao_lisp_frame_poly(prev);
+ }
+ if (ret)
+ break;
+ frame = prev;
+ }
+}
+
+const struct ao_lisp_type ao_lisp_frame_type = {
+ .mark = frame_mark,
+ .size = frame_size,
+ .move = frame_move,
+ .name = "frame",
+};
+
+void
+ao_lisp_frame_print(ao_poly p)
+{
+ struct ao_lisp_frame *frame = ao_lisp_poly_frame(p);
+ int f;
+
+ printf ("{");
+ if (frame) {
+ if (frame->type & AO_LISP_FRAME_PRINT)
+ printf("recurse...");
+ else {
+ frame->type |= AO_LISP_FRAME_PRINT;
+ for (f = 0; f < frame->num; f++) {
+ if (f != 0)
+ printf(", ");
+ ao_lisp_poly_print(frame->vals[f].atom);
+ printf(" = ");
+ ao_lisp_poly_print(frame->vals[f].val);
+ }
+ if (frame->prev)
+ ao_lisp_poly_print(frame->prev);
+ frame->type &= ~AO_LISP_FRAME_PRINT;
+ }
+ }
+ printf("}");
+}
+
+static int
+ao_lisp_frame_find(struct ao_lisp_frame *frame, int top, ao_poly atom)
+{
+ int l = 0;
+ int r = top - 1;
+ while (l <= r) {
+ int m = (l + r) >> 1;
+ if (frame->vals[m].atom < atom)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ return l;
+}
+
+ao_poly *
+ao_lisp_frame_ref(struct ao_lisp_frame *frame, ao_poly atom)
+{
+ int l = ao_lisp_frame_find(frame, frame->num, atom);
+
+ if (l >= frame->num)
+ return NULL;
+
+ if (frame->vals[l].atom != atom)
+ return NULL;
+ return &frame->vals[l].val;
+}
+
+int
+ao_lisp_frame_set(struct ao_lisp_frame *frame, ao_poly atom, ao_poly val)
+{
+ while (frame) {
+ if (!AO_LISP_IS_CONST(frame)) {
+ ao_poly *ref = ao_lisp_frame_ref(frame, atom);
+ if (ref) {
+ *ref = val;
+ return 1;
+ }
+ }
+ frame = ao_lisp_poly_frame(frame->prev);
+ }
+ return 0;
+}
+
+ao_poly
+ao_lisp_frame_get(struct ao_lisp_frame *frame, ao_poly atom)
+{
+ while (frame) {
+ ao_poly *ref = ao_lisp_frame_ref(frame, atom);
+ if (ref)
+ return *ref;
+ frame = ao_lisp_poly_frame(frame->prev);
+ }
+ return AO_LISP_NIL;
+}
+
+struct ao_lisp_frame *ao_lisp_frame_free_list[AO_LISP_FRAME_FREE];
+
+struct ao_lisp_frame *
+ao_lisp_frame_new(int num)
+{
+ struct ao_lisp_frame *frame;
+
+ if (num < AO_LISP_FRAME_FREE && (frame = ao_lisp_frame_free_list[num]))
+ ao_lisp_frame_free_list[num] = ao_lisp_poly_frame(frame->prev);
+ else {
+ frame = ao_lisp_alloc(frame_num_size(num));
+ if (!frame)
+ return NULL;
+ }
+ frame->type = AO_LISP_FRAME;
+ frame->num = num;
+ frame->prev = AO_LISP_NIL;
+ memset(frame->vals, '\0', num * sizeof (struct ao_lisp_val));
+ return frame;
+}
+
+ao_poly
+ao_lisp_frame_mark(struct ao_lisp_frame *frame)
+{
+ if (!frame)
+ return AO_LISP_NIL;
+ frame->type |= AO_LISP_FRAME_MARK;
+ return ao_lisp_frame_poly(frame);
+}
+
+void
+ao_lisp_frame_free(struct ao_lisp_frame *frame)
+{
+ if (!ao_lisp_frame_marked(frame)) {
+ int num = frame->num;
+ if (num < AO_LISP_FRAME_FREE) {
+ frame->prev = ao_lisp_frame_poly(ao_lisp_frame_free_list[num]);
+ ao_lisp_frame_free_list[num] = frame;
+ }
+ }
+}
+
+static struct ao_lisp_frame *
+ao_lisp_frame_realloc(struct ao_lisp_frame **frame_ref, int new_num)
+{
+ struct ao_lisp_frame *frame = *frame_ref;
+ struct ao_lisp_frame *new;
+ int copy;
+
+ if (new_num == frame->num)
+ return frame;
+ new = ao_lisp_frame_new(new_num);
+ if (!new)
+ return NULL;
+ /*
+ * Re-fetch the frame as it may have moved
+ * during the allocation
+ */
+ frame = *frame_ref;
+ copy = new_num;
+ if (copy > frame->num)
+ copy = frame->num;
+ memcpy(new->vals, frame->vals, copy * sizeof (struct ao_lisp_val));
+ new->prev = frame->prev;
+ ao_lisp_frame_free(frame);
+ return new;
+}
+
+void
+ao_lisp_frame_bind(struct ao_lisp_frame *frame, int num, ao_poly atom, ao_poly val)
+{
+ int l = ao_lisp_frame_find(frame, num, atom);
+
+ memmove(&frame->vals[l+1],
+ &frame->vals[l],
+ (num - l) * sizeof (struct ao_lisp_val));
+ frame->vals[l].atom = atom;
+ frame->vals[l].val = val;
+}
+
+int
+ao_lisp_frame_add(struct ao_lisp_frame **frame_ref, ao_poly atom, ao_poly val)
+{
+ struct ao_lisp_frame *frame = *frame_ref;
+ ao_poly *ref = frame ? ao_lisp_frame_ref(frame, atom) : NULL;
+
+ if (!ref) {
+ int f;
+ ao_lisp_poly_stash(0, atom);
+ ao_lisp_poly_stash(1, val);
+ if (frame) {
+ f = frame->num;
+ frame = ao_lisp_frame_realloc(frame_ref, f + 1);
+ } else {
+ f = 0;
+ frame = ao_lisp_frame_new(1);
+ }
+ if (!frame)
+ return 0;
+ *frame_ref = frame;
+ atom = ao_lisp_poly_fetch(0);
+ val = ao_lisp_poly_fetch(1);
+ ao_lisp_frame_bind(frame, frame->num - 1, atom, val);
+ } else
+ *ref = val;
+ return 1;
+}
diff --git a/src/lisp/ao_lisp_int.c b/src/lisp/ao_lisp_int.c
new file mode 100644
index 00000000..77f65e95
--- /dev/null
+++ b/src/lisp/ao_lisp_int.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+void
+ao_lisp_int_print(ao_poly p)
+{
+ int i = ao_lisp_poly_int(p);
+ printf("%d", i);
+}
diff --git a/src/lisp/ao_lisp_lambda.c b/src/lisp/ao_lisp_lambda.c
new file mode 100644
index 00000000..526863c5
--- /dev/null
+++ b/src/lisp/ao_lisp_lambda.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_lisp.h"
+
+int
+lambda_size(void *addr)
+{
+ (void) addr;
+ return sizeof (struct ao_lisp_lambda);
+}
+
+void
+lambda_mark(void *addr)
+{
+ struct ao_lisp_lambda *lambda = addr;
+
+ ao_lisp_poly_mark(lambda->code, 0);
+ ao_lisp_poly_mark(lambda->frame, 0);
+}
+
+void
+lambda_move(void *addr)
+{
+ struct ao_lisp_lambda *lambda = addr;
+
+ ao_lisp_poly_move(&lambda->code, 0);
+ ao_lisp_poly_move(&lambda->frame, 0);
+}
+
+const struct ao_lisp_type ao_lisp_lambda_type = {
+ .size = lambda_size,
+ .mark = lambda_mark,
+ .move = lambda_move,
+ .name = "lambda",
+};
+
+void
+ao_lisp_lambda_print(ao_poly poly)
+{
+ struct ao_lisp_lambda *lambda = ao_lisp_poly_lambda(poly);
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(lambda->code);
+
+ printf("(");
+ printf("%s", ao_lisp_args_name(lambda->args));
+ while (cons) {
+ printf(" ");
+ ao_lisp_poly_print(cons->car);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ printf(")");
+}
+
+ao_poly
+ao_lisp_lambda_alloc(struct ao_lisp_cons *code, int args)
+{
+ ao_lisp_cons_stash(0, code);
+ struct ao_lisp_lambda *lambda = ao_lisp_alloc(sizeof (struct ao_lisp_lambda));
+ code = ao_lisp_cons_fetch(0);
+ struct ao_lisp_cons *arg;
+ int f;
+
+ if (!lambda)
+ return AO_LISP_NIL;
+
+ if (!ao_lisp_check_argt(_ao_lisp_atom_lambda, code, 0, AO_LISP_CONS, 1))
+ return AO_LISP_NIL;
+ f = 0;
+ arg = ao_lisp_poly_cons(ao_lisp_arg(code, 0));
+ while (arg) {
+ if (ao_lisp_poly_type(arg->car) != AO_LISP_ATOM)
+ return ao_lisp_error(AO_LISP_INVALID, "formal %d is not an atom", f);
+ arg = ao_lisp_poly_cons(arg->cdr);
+ f++;
+ }
+
+ lambda->type = AO_LISP_LAMBDA;
+ lambda->args = args;
+ lambda->code = ao_lisp_cons_poly(code);
+ lambda->frame = ao_lisp_frame_mark(ao_lisp_frame_current);
+ DBGI("build frame: "); DBG_POLY(lambda->frame); DBG("\n");
+ DBG_STACK();
+ return ao_lisp_lambda_poly(lambda);
+}
+
+ao_poly
+ao_lisp_lambda(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_lambda_alloc(cons, AO_LISP_FUNC_LAMBDA);
+}
+
+ao_poly
+ao_lisp_lexpr(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_lambda_alloc(cons, AO_LISP_FUNC_LEXPR);
+}
+
+ao_poly
+ao_lisp_nlambda(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_lambda_alloc(cons, AO_LISP_FUNC_NLAMBDA);
+}
+
+ao_poly
+ao_lisp_macro(struct ao_lisp_cons *cons)
+{
+ return ao_lisp_lambda_alloc(cons, AO_LISP_FUNC_MACRO);
+}
+
+ao_poly
+ao_lisp_lambda_eval(void)
+{
+ struct ao_lisp_lambda *lambda = ao_lisp_poly_lambda(ao_lisp_v);
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(ao_lisp_stack->values);
+ struct ao_lisp_cons *code = ao_lisp_poly_cons(lambda->code);
+ struct ao_lisp_cons *args = ao_lisp_poly_cons(ao_lisp_arg(code, 0));
+ struct ao_lisp_frame *next_frame;
+ int args_wanted;
+ int args_provided;
+ int f;
+ struct ao_lisp_cons *vals;
+
+ DBGI("lambda "); DBG_POLY(ao_lisp_lambda_poly(lambda)); DBG("\n");
+
+ args_wanted = ao_lisp_cons_length(args);
+
+ /* Create a frame to hold the variables
+ */
+ args_provided = ao_lisp_cons_length(cons) - 1;
+ if (lambda->args == AO_LISP_FUNC_LAMBDA) {
+ if (args_wanted != args_provided)
+ return ao_lisp_error(AO_LISP_INVALID, "need %d args, got %d", args_wanted, args_provided);
+ } else {
+ if (args_provided < args_wanted - 1)
+ return ao_lisp_error(AO_LISP_INVALID, "need at least %d args, got %d", args_wanted, args_provided);
+ }
+
+ next_frame = ao_lisp_frame_new(args_wanted);
+
+ /* Re-fetch all of the values in case something moved */
+ lambda = ao_lisp_poly_lambda(ao_lisp_v);
+ cons = ao_lisp_poly_cons(ao_lisp_stack->values);
+ code = ao_lisp_poly_cons(lambda->code);
+ args = ao_lisp_poly_cons(ao_lisp_arg(code, 0));
+ vals = ao_lisp_poly_cons(cons->cdr);
+
+ next_frame->prev = lambda->frame;
+ ao_lisp_frame_current = next_frame;
+ ao_lisp_stack->frame = ao_lisp_frame_poly(ao_lisp_frame_current);
+
+ switch (lambda->args) {
+ case AO_LISP_FUNC_LAMBDA:
+ for (f = 0; f < args_wanted; f++) {
+ DBGI("bind "); DBG_POLY(args->car); DBG(" = "); DBG_POLY(vals->car); DBG("\n");
+ ao_lisp_frame_bind(next_frame, f, args->car, vals->car);
+ args = ao_lisp_poly_cons(args->cdr);
+ vals = ao_lisp_poly_cons(vals->cdr);
+ }
+ if (!ao_lisp_stack_marked(ao_lisp_stack))
+ ao_lisp_cons_free(cons);
+ cons = NULL;
+ break;
+ case AO_LISP_FUNC_LEXPR:
+ case AO_LISP_FUNC_NLAMBDA:
+ case AO_LISP_FUNC_MACRO:
+ for (f = 0; f < args_wanted - 1; f++) {
+ DBGI("bind "); DBG_POLY(args->car); DBG(" = "); DBG_POLY(vals->car); DBG("\n");
+ ao_lisp_frame_bind(next_frame, f, args->car, vals->car);
+ args = ao_lisp_poly_cons(args->cdr);
+ vals = ao_lisp_poly_cons(vals->cdr);
+ }
+ DBGI("bind "); DBG_POLY(args->car); DBG(" = "); DBG_POLY(ao_lisp_cons_poly(vals)); DBG("\n");
+ ao_lisp_frame_bind(next_frame, f, args->car, ao_lisp_cons_poly(vals));
+ break;
+ default:
+ break;
+ }
+ DBGI("eval frame: "); DBG_POLY(ao_lisp_frame_poly(next_frame)); DBG("\n");
+ DBG_STACK();
+ DBGI("eval code: "); DBG_POLY(code->cdr); DBG("\n");
+ return code->cdr;
+}
diff --git a/src/lisp/ao_lisp_lex.c b/src/lisp/ao_lisp_lex.c
new file mode 100644
index 00000000..fe7c47f4
--- /dev/null
+++ b/src/lisp/ao_lisp_lex.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
diff --git a/src/lisp/ao_lisp_make_const.c b/src/lisp/ao_lisp_make_const.c
new file mode 100644
index 00000000..49f989e6
--- /dev/null
+++ b/src/lisp/ao_lisp_make_const.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+
+static struct ao_lisp_builtin *
+ao_lisp_make_builtin(enum ao_lisp_builtin_id func, int args) {
+ struct ao_lisp_builtin *b = ao_lisp_alloc(sizeof (struct ao_lisp_builtin));
+
+ b->type = AO_LISP_BUILTIN;
+ b->func = func;
+ b->args = args;
+ return b;
+}
+
+struct builtin_func {
+ char *name;
+ int args;
+ int func;
+};
+
+struct builtin_func funcs[] = {
+ { .name = "eval", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_eval },
+ { .name = "read", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_read },
+ { .name = "lambda", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_lambda },
+ { .name = "lexpr", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_lexpr },
+ { .name = "nlambda", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_nlambda },
+ { .name = "macro", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_macro },
+ { .name = "car", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_car },
+ { .name = "cdr", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_cdr },
+ { .name = "cons", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_cons },
+ { .name = "last", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_last },
+ { .name = "length", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_length },
+ { .name = "quote", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_quote },
+ { .name = "set", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_set },
+ { .name = "setq", .args = AO_LISP_FUNC_MACRO, .func = builtin_setq },
+ { .name = "cond", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_cond },
+ { .name = "progn", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_progn },
+ { .name = "while", .args = AO_LISP_FUNC_NLAMBDA, .func = builtin_while },
+ { .name = "print", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_print },
+ { .name = "patom", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_patom },
+ { .name = "+", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_plus },
+ { .name = "-", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_minus },
+ { .name = "*", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_times },
+ { .name = "/", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_divide },
+ { .name = "%", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_mod },
+ { .name = "=", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_equal },
+ { .name = "<", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_less },
+ { .name = ">", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_greater },
+ { .name = "<=", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_less_equal },
+ { .name = ">=", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_greater_equal },
+ { .name = "pack", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_pack },
+ { .name = "unpack", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_unpack },
+ { .name = "flush", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_flush },
+ { .name = "delay", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_delay },
+ { .name = "led", .args = AO_LISP_FUNC_F_LEXPR, .func = builtin_led },
+ { .name = "save", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_save },
+ { .name = "restore", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_restore },
+ { .name = "call/cc", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_call_cc },
+ { .name = "collect", .args = AO_LISP_FUNC_F_LAMBDA, .func = builtin_collect },
+};
+
+#define N_FUNC (sizeof funcs / sizeof funcs[0])
+
+struct ao_lisp_frame *globals;
+
+static int
+is_atom(int offset)
+{
+ struct ao_lisp_atom *a;
+
+ for (a = ao_lisp_atoms; a; a = ao_lisp_poly_atom(a->next))
+ if (((uint8_t *) a->name - ao_lisp_const) == offset)
+ return strlen(a->name);
+ return 0;
+}
+
+#define AO_FEC_CRC_INIT 0xffff
+
+static inline uint16_t
+ao_fec_crc_byte(uint8_t byte, uint16_t crc)
+{
+ uint8_t bit;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (((crc & 0x8000) >> 8) ^ (byte & 0x80))
+ crc = (crc << 1) ^ 0x8005;
+ else
+ crc = (crc << 1);
+ byte <<= 1;
+ }
+ return crc;
+}
+
+uint16_t
+ao_fec_crc(const uint8_t *bytes, uint8_t len)
+{
+ uint16_t crc = AO_FEC_CRC_INIT;
+
+ while (len--)
+ crc = ao_fec_crc_byte(*bytes++, crc);
+ return crc;
+}
+
+struct ao_lisp_macro_stack {
+ struct ao_lisp_macro_stack *next;
+ ao_poly p;
+};
+
+struct ao_lisp_macro_stack *macro_stack;
+
+int
+ao_lisp_macro_push(ao_poly p)
+{
+ struct ao_lisp_macro_stack *m = macro_stack;
+
+ while (m) {
+ if (m->p == p)
+ return 1;
+ m = m->next;
+ }
+ m = malloc (sizeof (struct ao_lisp_macro_stack));
+ m->p = p;
+ m->next = macro_stack;
+ macro_stack = m;
+ return 0;
+}
+
+void
+ao_lisp_macro_pop(void)
+{
+ struct ao_lisp_macro_stack *m = macro_stack;
+
+ macro_stack = m->next;
+ free(m);
+}
+
+#define DBG_MACRO 0
+#if DBG_MACRO
+int macro_scan_depth;
+
+void indent(void)
+{
+ int i;
+ for (i = 0; i < macro_scan_depth; i++)
+ printf(" ");
+}
+#define MACRO_DEBUG(a) a
+#else
+#define MACRO_DEBUG(a)
+#endif
+
+ao_poly
+ao_has_macro(ao_poly p);
+
+ao_poly
+ao_macro_test_get(ao_poly atom)
+{
+ ao_poly *ref = ao_lisp_atom_ref(ao_lisp_frame_global, atom);
+ if (ref)
+ return *ref;
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_is_macro(ao_poly p)
+{
+ struct ao_lisp_builtin *builtin;
+ struct ao_lisp_lambda *lambda;
+ ao_poly ret;
+
+ MACRO_DEBUG(indent(); printf ("is macro "); ao_lisp_poly_print(p); printf("\n"); ++macro_scan_depth);
+ switch (ao_lisp_poly_type(p)) {
+ case AO_LISP_ATOM:
+ if (ao_lisp_macro_push(p))
+ ret = AO_LISP_NIL;
+ else {
+ if (ao_is_macro(ao_macro_test_get(p)))
+ ret = p;
+ else
+ ret = AO_LISP_NIL;
+ ao_lisp_macro_pop();
+ }
+ break;
+ case AO_LISP_CONS:
+ ret = ao_has_macro(p);
+ break;
+ case AO_LISP_BUILTIN:
+ builtin = ao_lisp_poly_builtin(p);
+ if ((builtin->args & AO_LISP_FUNC_MASK) == AO_LISP_FUNC_MACRO)
+ ret = p;
+ else
+ ret = 0;
+ break;
+
+ case AO_LISP_LAMBDA:
+ lambda = ao_lisp_poly_lambda(p);
+ if (lambda->args == AO_LISP_FUNC_MACRO)
+ ret = p;
+ else
+ ret = ao_has_macro(lambda->code);
+ break;
+ default:
+ ret = AO_LISP_NIL;
+ break;
+ }
+ MACRO_DEBUG(--macro_scan_depth; indent(); printf ("... "); ao_lisp_poly_print(ret); printf("\n"));
+ return ret;
+}
+
+ao_poly
+ao_has_macro(ao_poly p)
+{
+ struct ao_lisp_cons *cons;
+ struct ao_lisp_lambda *lambda;
+ ao_poly m;
+
+ if (p == AO_LISP_NIL)
+ return AO_LISP_NIL;
+
+ MACRO_DEBUG(indent(); printf("has macro "); ao_lisp_poly_print(p); printf("\n"); ++macro_scan_depth);
+ switch (ao_lisp_poly_type(p)) {
+ case AO_LISP_LAMBDA:
+ lambda = ao_lisp_poly_lambda(p);
+ p = ao_has_macro(lambda->code);
+ break;
+ case AO_LISP_CONS:
+ cons = ao_lisp_poly_cons(p);
+ if ((p = ao_is_macro(cons->car)))
+ break;
+
+ cons = ao_lisp_poly_cons(cons->cdr);
+ p = AO_LISP_NIL;
+ while (cons) {
+ m = ao_has_macro(cons->car);
+ if (m) {
+ p = m;
+ break;
+ }
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ break;
+
+ default:
+ p = AO_LISP_NIL;
+ break;
+ }
+ MACRO_DEBUG(--macro_scan_depth; indent(); printf("... "); ao_lisp_poly_print(p); printf("\n"));
+ return p;
+}
+
+int
+ao_lisp_read_eval_abort(void)
+{
+ ao_poly in, out = AO_LISP_NIL;
+ for(;;) {
+ in = ao_lisp_read();
+ if (in == _ao_lisp_atom_eof)
+ break;
+ out = ao_lisp_eval(in);
+ if (ao_lisp_exception)
+ return 0;
+ ao_lisp_poly_print(out);
+ putchar ('\n');
+ }
+ return 1;
+}
+
+static FILE *in;
+static FILE *out;
+
+int
+ao_lisp_getc(void)
+{
+ return getc(in);
+}
+
+static const struct option options[] = {
+ { .name = "out", .has_arg = 1, .val = 'o' },
+ { 0, 0, 0, 0 }
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--out=<output>] [input]\n", program);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int f, o;
+ ao_poly val;
+ struct ao_lisp_atom *a;
+ struct ao_lisp_builtin *b;
+ int in_atom = 0;
+ char *out_name = NULL;
+ int c;
+
+ in = stdin;
+ out = stdout;
+
+ while ((c = getopt_long(argc, argv, "o:", options, NULL)) != -1) {
+ switch (c) {
+ case 'o':
+ out_name = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ for (f = 0; f < (int) N_FUNC; f++) {
+ b = ao_lisp_make_builtin(funcs[f].func, funcs[f].args);
+ a = ao_lisp_atom_intern(funcs[f].name);
+ ao_lisp_atom_set(ao_lisp_atom_poly(a),
+ ao_lisp_builtin_poly(b));
+ }
+
+ /* boolean constants */
+ ao_lisp_atom_set(ao_lisp_atom_poly(ao_lisp_atom_intern("nil")),
+ AO_LISP_NIL);
+ a = ao_lisp_atom_intern("t");
+ ao_lisp_atom_set(ao_lisp_atom_poly(a),
+ ao_lisp_atom_poly(a));
+
+ /* end of file value */
+ a = ao_lisp_atom_intern("eof");
+ ao_lisp_atom_set(ao_lisp_atom_poly(a),
+ ao_lisp_atom_poly(a));
+
+ if (argv[optind]){
+ in = fopen(argv[optind], "r");
+ if (!in) {
+ perror(argv[optind]);
+ exit(1);
+ }
+ }
+ if (!ao_lisp_read_eval_abort()) {
+ fprintf(stderr, "eval failed\n");
+ exit(1);
+ }
+
+ /* Reduce to referenced values */
+ ao_lisp_collect(AO_LISP_COLLECT_FULL);
+
+ for (f = 0; f < ao_lisp_frame_global->num; f++) {
+ val = ao_has_macro(ao_lisp_frame_global->vals[f].val);
+ if (val != AO_LISP_NIL) {
+ printf("error: function %s contains unresolved macro: ",
+ ao_lisp_poly_atom(ao_lisp_frame_global->vals[f].atom)->name);
+ ao_lisp_poly_print(val);
+ printf("\n");
+ exit(1);
+ }
+ }
+
+ if (out_name) {
+ out = fopen(out_name, "w");
+ if (!out) {
+ perror(out_name);
+ exit(1);
+ }
+ }
+
+ fprintf(out, "/* Generated file, do not edit */\n\n");
+
+ fprintf(out, "#define AO_LISP_POOL_CONST %d\n", ao_lisp_top);
+ fprintf(out, "extern const uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute__((aligned(4)));\n");
+ fprintf(out, "#define ao_builtin_atoms 0x%04x\n", ao_lisp_atom_poly(ao_lisp_atoms));
+ fprintf(out, "#define ao_builtin_frame 0x%04x\n", ao_lisp_frame_poly(ao_lisp_frame_global));
+ fprintf(out, "#define ao_lisp_const_checksum ((uint16_t) 0x%04x)\n", ao_fec_crc(ao_lisp_const, ao_lisp_top));
+
+
+ for (a = ao_lisp_atoms; a; a = ao_lisp_poly_atom(a->next)) {
+ char *n = a->name, c;
+ fprintf(out, "#define _ao_lisp_atom_");
+ while ((c = *n++)) {
+ if (isalnum(c))
+ fprintf(out, "%c", c);
+ else
+ fprintf(out, "%02x", c);
+ }
+ fprintf(out, " 0x%04x\n", ao_lisp_atom_poly(a));
+ }
+ fprintf(out, "#ifdef AO_LISP_CONST_BITS\n");
+ fprintf(out, "const uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute((aligned(4))) = {");
+ for (o = 0; o < ao_lisp_top; o++) {
+ uint8_t c;
+ if ((o & 0xf) == 0)
+ fprintf(out, "\n\t");
+ else
+ fprintf(out, " ");
+ c = ao_lisp_const[o];
+ if (!in_atom)
+ in_atom = is_atom(o);
+ if (in_atom) {
+ fprintf(out, " '%c',", c);
+ in_atom--;
+ } else {
+ fprintf(out, "0x%02x,", c);
+ }
+ }
+ fprintf(out, "\n};\n");
+ fprintf(out, "#endif /* AO_LISP_CONST_BITS */\n");
+ exit(0);
+}
diff --git a/src/lisp/ao_lisp_mem.c b/src/lisp/ao_lisp_mem.c
new file mode 100644
index 00000000..d067ea07
--- /dev/null
+++ b/src/lisp/ao_lisp_mem.c
@@ -0,0 +1,880 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#define AO_LISP_CONST_BITS
+
+#include "ao_lisp.h"
+#include <stdio.h>
+
+#ifdef AO_LISP_MAKE_CONST
+
+/*
+ * When building the constant table, it is the
+ * pool for allocations.
+ */
+
+#include <stdlib.h>
+uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute__((aligned(4)));
+#define ao_lisp_pool ao_lisp_const
+#undef AO_LISP_POOL
+#define AO_LISP_POOL AO_LISP_POOL_CONST
+
+#else
+
+uint8_t ao_lisp_pool[AO_LISP_POOL + AO_LISP_POOL_EXTRA] __attribute__((aligned(4)));
+
+#endif
+
+#ifndef DBG_MEM_STATS
+#define DBG_MEM_STATS DBG_MEM
+#endif
+
+#if DBG_MEM
+int dbg_move_depth;
+int dbg_mem = DBG_MEM_START;
+int dbg_validate = 0;
+
+struct ao_lisp_record {
+ struct ao_lisp_record *next;
+ const struct ao_lisp_type *type;
+ void *addr;
+ int size;
+};
+
+static struct ao_lisp_record *record_head, **record_tail;
+
+static void
+ao_lisp_record_free(struct ao_lisp_record *record)
+{
+ while (record) {
+ struct ao_lisp_record *next = record->next;
+ free(record);
+ record = next;
+ }
+}
+
+static void
+ao_lisp_record_reset(void)
+{
+ ao_lisp_record_free(record_head);
+ record_head = NULL;
+ record_tail = &record_head;
+}
+
+static void
+ao_lisp_record(const struct ao_lisp_type *type,
+ void *addr,
+ int size)
+{
+ struct ao_lisp_record *r = malloc(sizeof (struct ao_lisp_record));
+
+ r->next = NULL;
+ r->type = type;
+ r->addr = addr;
+ r->size = size;
+ *record_tail = r;
+ record_tail = &r->next;
+}
+
+static struct ao_lisp_record *
+ao_lisp_record_save(void)
+{
+ struct ao_lisp_record *r = record_head;
+
+ record_head = NULL;
+ record_tail = &record_head;
+ return r;
+}
+
+static void
+ao_lisp_record_compare(char *where,
+ struct ao_lisp_record *a,
+ struct ao_lisp_record *b)
+{
+ while (a && b) {
+ if (a->type != b->type || a->size != b->size) {
+ printf("%s record difers %d %s %d -> %d %s %d\n",
+ where,
+ MDBG_OFFSET(a->addr),
+ a->type->name,
+ a->size,
+ MDBG_OFFSET(b->addr),
+ b->type->name,
+ b->size);
+ ao_lisp_abort();
+ }
+ a = a->next;
+ b = b->next;
+ }
+ if (a) {
+ printf("%s record differs %d %s %d -> NULL\n",
+ where,
+ MDBG_OFFSET(a->addr),
+ a->type->name,
+ a->size);
+ ao_lisp_abort();
+ }
+ if (b) {
+ printf("%s record differs NULL -> %d %s %d\n",
+ where,
+ MDBG_OFFSET(b->addr),
+ b->type->name,
+ b->size);
+ ao_lisp_abort();
+ }
+}
+
+#else
+#define ao_lisp_record_reset()
+#endif
+
+uint8_t ao_lisp_exception;
+
+struct ao_lisp_root {
+ const struct ao_lisp_type *type;
+ void **addr;
+};
+
+static struct ao_lisp_cons *save_cons[2];
+static char *save_string[2];
+static ao_poly save_poly[3];
+
+static const struct ao_lisp_root ao_lisp_root[] = {
+ {
+ .type = &ao_lisp_cons_type,
+ .addr = (void **) &save_cons[0],
+ },
+ {
+ .type = &ao_lisp_cons_type,
+ .addr = (void **) &save_cons[1],
+ },
+ {
+ .type = &ao_lisp_string_type,
+ .addr = (void **) &save_string[0],
+ },
+ {
+ .type = &ao_lisp_string_type,
+ .addr = (void **) &save_string[1],
+ },
+ {
+ .type = NULL,
+ .addr = (void **) (void *) &save_poly[0]
+ },
+ {
+ .type = NULL,
+ .addr = (void **) (void *) &save_poly[1]
+ },
+ {
+ .type = NULL,
+ .addr = (void **) (void *) &save_poly[2]
+ },
+ {
+ .type = &ao_lisp_atom_type,
+ .addr = (void **) &ao_lisp_atoms
+ },
+ {
+ .type = &ao_lisp_frame_type,
+ .addr = (void **) &ao_lisp_frame_global,
+ },
+ {
+ .type = &ao_lisp_frame_type,
+ .addr = (void **) &ao_lisp_frame_current,
+ },
+ {
+ .type = &ao_lisp_stack_type,
+ .addr = (void **) &ao_lisp_stack,
+ },
+ {
+ .type = NULL,
+ .addr = (void **) (void *) &ao_lisp_v,
+ },
+ {
+ .type = &ao_lisp_cons_type,
+ .addr = (void **) &ao_lisp_read_cons,
+ },
+ {
+ .type = &ao_lisp_cons_type,
+ .addr = (void **) &ao_lisp_read_cons_tail,
+ },
+ {
+ .type = &ao_lisp_cons_type,
+ .addr = (void **) &ao_lisp_read_stack,
+ },
+};
+
+#define AO_LISP_ROOT (sizeof (ao_lisp_root) / sizeof (ao_lisp_root[0]))
+
+static const void ** const ao_lisp_cache[] = {
+ (const void **) &ao_lisp_cons_free_list,
+ (const void **) &ao_lisp_stack_free_list,
+ (const void **) &ao_lisp_frame_free_list[0],
+ (const void **) &ao_lisp_frame_free_list[1],
+ (const void **) &ao_lisp_frame_free_list[2],
+ (const void **) &ao_lisp_frame_free_list[3],
+ (const void **) &ao_lisp_frame_free_list[4],
+ (const void **) &ao_lisp_frame_free_list[5],
+};
+
+#if AO_LISP_FRAME_FREE != 6
+#error Unexpected AO_LISP_FRAME_FREE value
+#endif
+
+#define AO_LISP_CACHE (sizeof (ao_lisp_cache) / sizeof (ao_lisp_cache[0]))
+
+#define AO_LISP_BUSY_SIZE ((AO_LISP_POOL + 31) / 32)
+
+static uint8_t ao_lisp_busy[AO_LISP_BUSY_SIZE];
+static uint8_t ao_lisp_cons_note[AO_LISP_BUSY_SIZE];
+static uint8_t ao_lisp_cons_last[AO_LISP_BUSY_SIZE];
+static uint8_t ao_lisp_cons_noted;
+
+uint16_t ao_lisp_top;
+
+struct ao_lisp_chunk {
+ uint16_t old_offset;
+ union {
+ uint16_t size;
+ uint16_t new_offset;
+ };
+};
+
+#define AO_LISP_NCHUNK 64
+
+static struct ao_lisp_chunk ao_lisp_chunk[AO_LISP_NCHUNK];
+
+/* Offset of an address within the pool. */
+static inline uint16_t pool_offset(void *addr) {
+#if DBG_MEM
+ if (!AO_LISP_IS_POOL(addr))
+ ao_lisp_abort();
+#endif
+ return ((uint8_t *) addr) - ao_lisp_pool;
+}
+
+static inline void mark(uint8_t *tag, int offset) {
+ int byte = offset >> 5;
+ int bit = (offset >> 2) & 7;
+ tag[byte] |= (1 << bit);
+}
+
+static inline void clear(uint8_t *tag, int offset) {
+ int byte = offset >> 5;
+ int bit = (offset >> 2) & 7;
+ tag[byte] &= ~(1 << bit);
+}
+
+static inline int busy(uint8_t *tag, int offset) {
+ int byte = offset >> 5;
+ int bit = (offset >> 2) & 7;
+ return (tag[byte] >> bit) & 1;
+}
+
+static inline int min(int a, int b) { return a < b ? a : b; }
+static inline int max(int a, int b) { return a > b ? a : b; }
+
+static inline int limit(int offset) {
+ return min(AO_LISP_POOL, max(offset, 0));
+}
+
+static void
+note_cons(uint16_t offset)
+{
+ MDBG_MOVE("note cons %d\n", offset);
+ ao_lisp_cons_noted = 1;
+ mark(ao_lisp_cons_note, offset);
+}
+
+static uint16_t chunk_low, chunk_high;
+static uint16_t chunk_first, chunk_last;
+
+static int
+find_chunk(uint16_t offset)
+{
+ int l, r;
+ /* Binary search for the location */
+ l = chunk_first;
+ r = chunk_last - 1;
+ while (l <= r) {
+ int m = (l + r) >> 1;
+ if (ao_lisp_chunk[m].old_offset < offset)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ return l;
+}
+
+static void
+note_chunk(uint16_t offset, uint16_t size)
+{
+ int l;
+
+ if (offset < chunk_low || chunk_high <= offset)
+ return;
+
+ l = find_chunk(offset);
+
+ /*
+ * The correct location is always in 'l', with r = l-1 being
+ * the entry before the right one
+ */
+
+#if DBG_MEM
+ /* Off the right side */
+ if (l >= AO_LISP_NCHUNK)
+ ao_lisp_abort();
+
+ /* Off the left side */
+ if (l == 0 && chunk_last && offset > ao_lisp_chunk[0].old_offset)
+ ao_lisp_abort();
+#endif
+
+ /* Shuffle existing entries right */
+ int end = min(AO_LISP_NCHUNK, chunk_last + 1);
+
+ memmove(&ao_lisp_chunk[l+1],
+ &ao_lisp_chunk[l],
+ (end - (l+1)) * sizeof (struct ao_lisp_chunk));
+
+ /* Add new entry */
+ ao_lisp_chunk[l].old_offset = offset;
+ ao_lisp_chunk[l].size = size;
+
+ /* Increment the number of elements up to the size of the array */
+ if (chunk_last < AO_LISP_NCHUNK)
+ chunk_last++;
+
+ /* Set the top address if the array is full */
+ if (chunk_last == AO_LISP_NCHUNK)
+ chunk_high = ao_lisp_chunk[AO_LISP_NCHUNK-1].old_offset +
+ ao_lisp_chunk[AO_LISP_NCHUNK-1].size;
+}
+
+static void
+reset_chunks(void)
+{
+ chunk_high = ao_lisp_top;
+ chunk_last = 0;
+ chunk_first = 0;
+}
+
+/*
+ * Walk all referenced objects calling functions on each one
+ */
+
+static void
+walk(int (*visit_addr)(const struct ao_lisp_type *type, void **addr),
+ int (*visit_poly)(ao_poly *p, uint8_t do_note_cons))
+{
+ int i;
+
+ ao_lisp_record_reset();
+ memset(ao_lisp_busy, '\0', sizeof (ao_lisp_busy));
+ memset(ao_lisp_cons_note, '\0', sizeof (ao_lisp_cons_note));
+ ao_lisp_cons_noted = 0;
+ for (i = 0; i < (int) AO_LISP_ROOT; i++) {
+ if (ao_lisp_root[i].type) {
+ void **a = ao_lisp_root[i].addr, *v;
+ if (a && (v = *a)) {
+ MDBG_MOVE("root ptr %d\n", MDBG_OFFSET(v));
+ visit_addr(ao_lisp_root[i].type, a);
+ }
+ } else {
+ ao_poly *a = (ao_poly *) ao_lisp_root[i].addr, p;
+ if (a && (p = *a)) {
+ MDBG_MOVE("root poly %d\n", MDBG_OFFSET(ao_lisp_ref(p)));
+ visit_poly(a, 0);
+ }
+ }
+ }
+ while (ao_lisp_cons_noted) {
+ memcpy(ao_lisp_cons_last, ao_lisp_cons_note, sizeof (ao_lisp_cons_note));
+ memset(ao_lisp_cons_note, '\0', sizeof (ao_lisp_cons_note));
+ ao_lisp_cons_noted = 0;
+ for (i = 0; i < AO_LISP_POOL; i += 4) {
+ if (busy(ao_lisp_cons_last, i)) {
+ void *v = ao_lisp_pool + i;
+ MDBG_MOVE("root cons %d\n", MDBG_OFFSET(v));
+ visit_addr(&ao_lisp_cons_type, &v);
+ }
+ }
+ }
+}
+
+#if MDBG_DUMP
+static void
+dump_busy(void)
+{
+ int i;
+ MDBG_MOVE("busy:");
+ for (i = 0; i < ao_lisp_top; i += 4) {
+ if ((i & 0xff) == 0) {
+ MDBG_MORE("\n");
+ MDBG_MOVE("%s", "");
+ }
+ else if ((i & 0x1f) == 0)
+ MDBG_MORE(" ");
+ if (busy(ao_lisp_busy, i))
+ MDBG_MORE("*");
+ else
+ MDBG_MORE("-");
+ }
+ MDBG_MORE ("\n");
+}
+#define DUMP_BUSY() dump_busy()
+#else
+#define DUMP_BUSY()
+#endif
+
+static const struct ao_lisp_type const *ao_lisp_types[AO_LISP_NUM_TYPE] = {
+ [AO_LISP_CONS] = &ao_lisp_cons_type,
+ [AO_LISP_INT] = NULL,
+ [AO_LISP_STRING] = &ao_lisp_string_type,
+ [AO_LISP_OTHER] = (void *) 0x1,
+ [AO_LISP_ATOM] = &ao_lisp_atom_type,
+ [AO_LISP_BUILTIN] = &ao_lisp_builtin_type,
+ [AO_LISP_FRAME] = &ao_lisp_frame_type,
+ [AO_LISP_LAMBDA] = &ao_lisp_lambda_type,
+ [AO_LISP_STACK] = &ao_lisp_stack_type,
+};
+
+static int
+ao_lisp_mark_ref(const struct ao_lisp_type *type, void **ref)
+{
+ return ao_lisp_mark(type, *ref);
+}
+
+static int
+ao_lisp_poly_mark_ref(ao_poly *p, uint8_t do_note_cons)
+{
+ return ao_lisp_poly_mark(*p, do_note_cons);
+}
+
+#if DBG_MEM_STATS
+int ao_lisp_collects[2];
+int ao_lisp_freed[2];
+int ao_lisp_loops[2];
+#endif
+
+int ao_lisp_last_top;
+
+int
+ao_lisp_collect(uint8_t style)
+{
+ int i;
+ int top;
+#if DBG_MEM_STATS
+ int loops = 0;
+#endif
+#if DBG_MEM
+ struct ao_lisp_record *mark_record = NULL, *move_record = NULL;
+
+ MDBG_MOVE("collect %d\n", ao_lisp_collects[style]);
+#endif
+
+ /* The first time through, we're doing a full collect */
+ if (ao_lisp_last_top == 0)
+ style = AO_LISP_COLLECT_FULL;
+
+ /* Clear references to all caches */
+ for (i = 0; i < (int) AO_LISP_CACHE; i++)
+ *ao_lisp_cache[i] = NULL;
+ if (style == AO_LISP_COLLECT_FULL) {
+ chunk_low = top = 0;
+ } else {
+ chunk_low = top = ao_lisp_last_top;
+ }
+ for (;;) {
+#if DBG_MEM_STATS
+ loops++;
+#endif
+ MDBG_MOVE("move chunks from %d to %d\n", chunk_low, top);
+ /* Find the sizes of the first chunk of objects to move */
+ reset_chunks();
+ walk(ao_lisp_mark_ref, ao_lisp_poly_mark_ref);
+#if DBG_MEM
+
+ ao_lisp_record_free(mark_record);
+ mark_record = ao_lisp_record_save();
+ if (mark_record && move_record)
+ ao_lisp_record_compare("mark", move_record, mark_record);
+#endif
+
+ DUMP_BUSY();
+
+ /* Find the first moving object */
+ for (i = 0; i < chunk_last; i++) {
+ uint16_t size = ao_lisp_chunk[i].size;
+
+#if DBG_MEM
+ if (!size)
+ ao_lisp_abort();
+#endif
+
+ if (ao_lisp_chunk[i].old_offset > top)
+ break;
+
+ MDBG_MOVE("chunk %d %d not moving\n",
+ ao_lisp_chunk[i].old_offset,
+ ao_lisp_chunk[i].size);
+#if DBG_MEM
+ if (ao_lisp_chunk[i].old_offset != top)
+ ao_lisp_abort();
+#endif
+ top += size;
+ }
+
+ /*
+ * Limit amount of chunk array used in mapping moves
+ * to the active region
+ */
+ chunk_first = i;
+ chunk_low = ao_lisp_chunk[i].old_offset;
+
+ /* Copy all of the objects */
+ for (; i < chunk_last; i++) {
+ uint16_t size = ao_lisp_chunk[i].size;
+
+#if DBG_MEM
+ if (!size)
+ ao_lisp_abort();
+#endif
+
+ MDBG_MOVE("chunk %d %d -> %d\n",
+ ao_lisp_chunk[i].old_offset,
+ size,
+ top);
+ ao_lisp_chunk[i].new_offset = top;
+
+ memmove(&ao_lisp_pool[top],
+ &ao_lisp_pool[ao_lisp_chunk[i].old_offset],
+ size);
+
+ top += size;
+ }
+
+ if (chunk_first < chunk_last) {
+ /* Relocate all references to the objects */
+ walk(ao_lisp_move, ao_lisp_poly_move);
+
+#if DBG_MEM
+ ao_lisp_record_free(move_record);
+ move_record = ao_lisp_record_save();
+ if (mark_record && move_record)
+ ao_lisp_record_compare("move", mark_record, move_record);
+#endif
+ }
+
+ /* If we ran into the end of the heap, then
+ * there's no need to keep walking
+ */
+ if (chunk_last != AO_LISP_NCHUNK)
+ break;
+
+ /* Next loop starts right above this loop */
+ chunk_low = chunk_high;
+ }
+
+#if DBG_MEM_STATS
+ /* Collect stats */
+ ++ao_lisp_collects[style];
+ ao_lisp_freed[style] += ao_lisp_top - top;
+ ao_lisp_loops[style] += loops;
+#endif
+
+ ao_lisp_top = top;
+ if (style == AO_LISP_COLLECT_FULL)
+ ao_lisp_last_top = top;
+
+ MDBG_DO(memset(ao_lisp_chunk, '\0', sizeof (ao_lisp_chunk));
+ walk(ao_lisp_mark_ref, ao_lisp_poly_mark_ref));
+
+ return AO_LISP_POOL - ao_lisp_top;
+}
+
+/*
+ * Mark interfaces for objects
+ */
+
+/*
+ * Note a reference to memory and collect information about a few
+ * object sizes at a time
+ */
+
+int
+ao_lisp_mark_memory(const struct ao_lisp_type *type, void *addr)
+{
+ int offset;
+ if (!AO_LISP_IS_POOL(addr))
+ return 1;
+
+ offset = pool_offset(addr);
+ MDBG_MOVE("mark memory %d\n", MDBG_OFFSET(addr));
+ if (busy(ao_lisp_busy, offset)) {
+ MDBG_MOVE("already marked\n");
+ return 1;
+ }
+ mark(ao_lisp_busy, offset);
+ note_chunk(offset, ao_lisp_size(type, addr));
+ return 0;
+}
+
+/*
+ * Mark an object and all that it refereces
+ */
+int
+ao_lisp_mark(const struct ao_lisp_type *type, void *addr)
+{
+ int ret;
+ MDBG_MOVE("mark %d\n", MDBG_OFFSET(addr));
+ MDBG_MOVE_IN();
+ ret = ao_lisp_mark_memory(type, addr);
+ if (!ret) {
+ MDBG_MOVE("mark recurse\n");
+ type->mark(addr);
+ }
+ MDBG_MOVE_OUT();
+ return ret;
+}
+
+/*
+ * Mark an object, unless it is a cons cell and
+ * do_note_cons is set. In that case, just
+ * set a bit in the cons note array; those
+ * will be marked in a separate pass to avoid
+ * deep recursion in the collector
+ */
+int
+ao_lisp_poly_mark(ao_poly p, uint8_t do_note_cons)
+{
+ uint8_t type;
+ void *addr;
+
+ type = ao_lisp_poly_base_type(p);
+
+ if (type == AO_LISP_INT)
+ return 1;
+
+ addr = ao_lisp_ref(p);
+ if (!AO_LISP_IS_POOL(addr))
+ return 1;
+
+ if (type == AO_LISP_CONS && do_note_cons) {
+ note_cons(pool_offset(addr));
+ return 1;
+ } else {
+ if (type == AO_LISP_OTHER)
+ type = ao_lisp_other_type(addr);
+
+ const struct ao_lisp_type *lisp_type = ao_lisp_types[type];
+#if DBG_MEM
+ if (!lisp_type)
+ ao_lisp_abort();
+#endif
+
+ return ao_lisp_mark(lisp_type, addr);
+ }
+}
+
+/*
+ * Find the current location of an object
+ * based on the original location. For unmoved
+ * objects, this is simple. For moved objects,
+ * go search for it
+ */
+
+static uint16_t
+move_map(uint16_t offset)
+{
+ int l;
+
+ if (offset < chunk_low || chunk_high <= offset)
+ return offset;
+
+ l = find_chunk(offset);
+
+#if DBG_MEM
+ if (ao_lisp_chunk[l].old_offset != offset)
+ ao_lisp_abort();
+#endif
+ return ao_lisp_chunk[l].new_offset;
+}
+
+int
+ao_lisp_move_memory(const struct ao_lisp_type *type, void **ref)
+{
+ void *addr = *ref;
+ uint16_t offset, orig_offset;
+
+ if (!AO_LISP_IS_POOL(addr))
+ return 1;
+
+ (void) type;
+
+ MDBG_MOVE("move memory %d\n", MDBG_OFFSET(addr));
+ orig_offset = pool_offset(addr);
+ offset = move_map(orig_offset);
+ if (offset != orig_offset) {
+ MDBG_MOVE("update ref %d %d -> %d\n",
+ AO_LISP_IS_POOL(ref) ? MDBG_OFFSET(ref) : -1,
+ orig_offset, offset);
+ *ref = ao_lisp_pool + offset;
+ }
+ if (busy(ao_lisp_busy, offset)) {
+ MDBG_MOVE("already moved\n");
+ return 1;
+ }
+ mark(ao_lisp_busy, offset);
+ MDBG_DO(ao_lisp_record(type, addr, ao_lisp_size(type, addr)));
+ return 0;
+}
+
+int
+ao_lisp_move(const struct ao_lisp_type *type, void **ref)
+{
+ int ret;
+ MDBG_MOVE("move object %d\n", MDBG_OFFSET(*ref));
+ MDBG_MOVE_IN();
+ ret = ao_lisp_move_memory(type, ref);
+ if (!ret) {
+ MDBG_MOVE("move recurse\n");
+ type->move(*ref);
+ }
+ MDBG_MOVE_OUT();
+ return ret;
+}
+
+int
+ao_lisp_poly_move(ao_poly *ref, uint8_t do_note_cons)
+{
+ uint8_t type;
+ ao_poly p = *ref;
+ int ret;
+ void *addr;
+ uint16_t offset, orig_offset;
+ uint8_t base_type;
+
+ base_type = type = ao_lisp_poly_base_type(p);
+
+ if (type == AO_LISP_INT)
+ return 1;
+
+ addr = ao_lisp_ref(p);
+ if (!AO_LISP_IS_POOL(addr))
+ return 1;
+
+ orig_offset = pool_offset(addr);
+ offset = move_map(orig_offset);
+
+ if (type == AO_LISP_CONS && do_note_cons) {
+ note_cons(orig_offset);
+ ret = 1;
+ } else {
+ if (type == AO_LISP_OTHER)
+ type = ao_lisp_other_type(ao_lisp_pool + offset);
+
+ const struct ao_lisp_type *lisp_type = ao_lisp_types[type];
+#if DBG_MEM
+ if (!lisp_type)
+ ao_lisp_abort();
+#endif
+
+ ret = ao_lisp_move(lisp_type, &addr);
+ }
+
+ /* Re-write the poly value */
+ if (offset != orig_offset) {
+ ao_poly np = ao_lisp_poly(ao_lisp_pool + offset, base_type);
+ MDBG_MOVE("poly %d moved %d -> %d\n",
+ type, orig_offset, offset);
+ *ref = np;
+ }
+ return ret;
+}
+
+#if DBG_MEM
+void
+ao_lisp_validate(void)
+{
+ chunk_low = 0;
+ memset(ao_lisp_chunk, '\0', sizeof (ao_lisp_chunk));
+ walk(ao_lisp_mark_ref, ao_lisp_poly_mark_ref);
+}
+
+int dbg_allocs;
+
+#endif
+
+void *
+ao_lisp_alloc(int size)
+{
+ void *addr;
+
+ MDBG_DO(++dbg_allocs);
+ MDBG_DO(if (dbg_validate) ao_lisp_validate());
+ size = ao_lisp_size_round(size);
+ if (AO_LISP_POOL - ao_lisp_top < size &&
+ ao_lisp_collect(AO_LISP_COLLECT_INCREMENTAL) < size &&
+ ao_lisp_collect(AO_LISP_COLLECT_FULL) < size)
+ {
+ ao_lisp_error(AO_LISP_OOM, "out of memory");
+ return NULL;
+ }
+ addr = ao_lisp_pool + ao_lisp_top;
+ ao_lisp_top += size;
+ return addr;
+}
+
+void
+ao_lisp_cons_stash(int id, struct ao_lisp_cons *cons)
+{
+ save_cons[id] = cons;
+}
+
+struct ao_lisp_cons *
+ao_lisp_cons_fetch(int id)
+{
+ struct ao_lisp_cons *cons = save_cons[id];
+ save_cons[id] = NULL;
+ return cons;
+}
+
+void
+ao_lisp_poly_stash(int id, ao_poly poly)
+{
+ save_poly[id] = poly;
+}
+
+ao_poly
+ao_lisp_poly_fetch(int id)
+{
+ ao_poly poly = save_poly[id];
+ save_poly[id] = AO_LISP_NIL;
+ return poly;
+}
+
+void
+ao_lisp_string_stash(int id, char *string)
+{
+ save_string[id] = string;
+}
+
+char *
+ao_lisp_string_fetch(int id)
+{
+ char *string = save_string[id];
+ save_string[id] = NULL;
+ return string;
+}
+
diff --git a/src/lisp/ao_lisp_os.h b/src/lisp/ao_lisp_os.h
new file mode 100644
index 00000000..5fa3686b
--- /dev/null
+++ b/src/lisp/ao_lisp_os.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LISP_OS_H_
+#define _AO_LISP_OS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+extern int ao_lisp_getc(void);
+
+static inline void
+ao_lisp_os_flush(void) {
+ fflush(stdout);
+}
+
+static inline void
+ao_lisp_abort(void)
+{
+ abort();
+}
+
+static inline void
+ao_lisp_os_led(int led)
+{
+ printf("leds set to 0x%x\n", led);
+}
+
+static inline void
+ao_lisp_os_delay(int delay)
+{
+ struct timespec ts = {
+ .tv_sec = delay / 1000,
+ .tv_nsec = (delay % 1000) * 1000000,
+ };
+ nanosleep(&ts, NULL);
+}
+#endif
diff --git a/src/lisp/ao_lisp_poly.c b/src/lisp/ao_lisp_poly.c
new file mode 100644
index 00000000..fb3b06fe
--- /dev/null
+++ b/src/lisp/ao_lisp_poly.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+struct ao_lisp_funcs {
+ void (*print)(ao_poly);
+ void (*patom)(ao_poly);
+};
+
+static const struct ao_lisp_funcs ao_lisp_funcs[AO_LISP_NUM_TYPE] = {
+ [AO_LISP_CONS] = {
+ .print = ao_lisp_cons_print,
+ .patom = ao_lisp_cons_patom,
+ },
+ [AO_LISP_STRING] = {
+ .print = ao_lisp_string_print,
+ .patom = ao_lisp_string_patom,
+ },
+ [AO_LISP_INT] = {
+ .print = ao_lisp_int_print,
+ .patom = ao_lisp_int_print,
+ },
+ [AO_LISP_ATOM] = {
+ .print = ao_lisp_atom_print,
+ .patom = ao_lisp_atom_print,
+ },
+ [AO_LISP_BUILTIN] = {
+ .print = ao_lisp_builtin_print,
+ .patom = ao_lisp_builtin_print,
+ },
+ [AO_LISP_FRAME] = {
+ .print = ao_lisp_frame_print,
+ .patom = ao_lisp_frame_print,
+ },
+ [AO_LISP_LAMBDA] = {
+ .print = ao_lisp_lambda_print,
+ .patom = ao_lisp_lambda_print,
+ },
+ [AO_LISP_STACK] = {
+ .print = ao_lisp_stack_print,
+ .patom = ao_lisp_stack_print,
+ },
+};
+
+static const struct ao_lisp_funcs *
+funcs(ao_poly p)
+{
+ uint8_t type = ao_lisp_poly_type(p);
+
+ if (type < AO_LISP_NUM_TYPE)
+ return &ao_lisp_funcs[type];
+ return NULL;
+}
+
+void
+ao_lisp_poly_print(ao_poly p)
+{
+ const struct ao_lisp_funcs *f = funcs(p);
+
+ if (f && f->print)
+ f->print(p);
+}
+
+void
+ao_lisp_poly_patom(ao_poly p)
+{
+ const struct ao_lisp_funcs *f = funcs(p);
+
+ if (f && f->patom)
+ f->patom(p);
+}
+
+void *
+ao_lisp_ref(ao_poly poly) {
+ if (poly == AO_LISP_NIL)
+ return NULL;
+ if (poly & AO_LISP_CONST)
+ return (void *) (ao_lisp_const + (poly & AO_LISP_REF_MASK) - 4);
+ return (void *) (ao_lisp_pool + (poly & AO_LISP_REF_MASK) - 4);
+}
+
+ao_poly
+ao_lisp_poly(const void *addr, ao_poly type) {
+ const uint8_t *a = addr;
+ if (a == NULL)
+ return AO_LISP_NIL;
+ if (AO_LISP_IS_CONST(a))
+ return AO_LISP_CONST | (a - ao_lisp_const + 4) | type;
+ return (a - ao_lisp_pool + 4) | type;
+}
diff --git a/src/lisp/ao_lisp_read.c b/src/lisp/ao_lisp_read.c
new file mode 100644
index 00000000..84ef2a61
--- /dev/null
+++ b/src/lisp/ao_lisp_read.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+#include "ao_lisp_read.h"
+
+static const uint16_t lex_classes[128] = {
+ IGNORE, /* ^@ */
+ IGNORE, /* ^A */
+ IGNORE, /* ^B */
+ IGNORE, /* ^C */
+ IGNORE, /* ^D */
+ IGNORE, /* ^E */
+ IGNORE, /* ^F */
+ IGNORE, /* ^G */
+ IGNORE, /* ^H */
+ WHITE, /* ^I */
+ WHITE, /* ^J */
+ WHITE, /* ^K */
+ WHITE, /* ^L */
+ WHITE, /* ^M */
+ IGNORE, /* ^N */
+ IGNORE, /* ^O */
+ IGNORE, /* ^P */
+ IGNORE, /* ^Q */
+ IGNORE, /* ^R */
+ IGNORE, /* ^S */
+ IGNORE, /* ^T */
+ IGNORE, /* ^U */
+ IGNORE, /* ^V */
+ IGNORE, /* ^W */
+ IGNORE, /* ^X */
+ IGNORE, /* ^Y */
+ IGNORE, /* ^Z */
+ IGNORE, /* ^[ */
+ IGNORE, /* ^\ */
+ IGNORE, /* ^] */
+ IGNORE, /* ^^ */
+ IGNORE, /* ^_ */
+ PRINTABLE|WHITE, /* */
+ PRINTABLE, /* ! */
+ PRINTABLE|STRINGC, /* " */
+ PRINTABLE|COMMENT, /* # */
+ PRINTABLE, /* $ */
+ PRINTABLE, /* % */
+ PRINTABLE, /* & */
+ PRINTABLE|QUOTEC, /* ' */
+ PRINTABLE|BRA, /* ( */
+ PRINTABLE|KET, /* ) */
+ PRINTABLE, /* * */
+ PRINTABLE|SIGN, /* + */
+ PRINTABLE, /* , */
+ PRINTABLE|SIGN, /* - */
+ PRINTABLE, /* . */
+ PRINTABLE, /* / */
+ PRINTABLE|DIGIT, /* 0 */
+ PRINTABLE|DIGIT, /* 1 */
+ PRINTABLE|DIGIT, /* 2 */
+ PRINTABLE|DIGIT, /* 3 */
+ PRINTABLE|DIGIT, /* 4 */
+ PRINTABLE|DIGIT, /* 5 */
+ PRINTABLE|DIGIT, /* 6 */
+ PRINTABLE|DIGIT, /* 7 */
+ PRINTABLE|DIGIT, /* 8 */
+ PRINTABLE|DIGIT, /* 9 */
+ PRINTABLE, /* : */
+ PRINTABLE|COMMENT, /* ; */
+ PRINTABLE, /* < */
+ PRINTABLE, /* = */
+ PRINTABLE, /* > */
+ PRINTABLE, /* ? */
+ PRINTABLE, /* @ */
+ PRINTABLE, /* A */
+ PRINTABLE, /* B */
+ PRINTABLE, /* C */
+ PRINTABLE, /* D */
+ PRINTABLE, /* E */
+ PRINTABLE, /* F */
+ PRINTABLE, /* G */
+ PRINTABLE, /* H */
+ PRINTABLE, /* I */
+ PRINTABLE, /* J */
+ PRINTABLE, /* K */
+ PRINTABLE, /* L */
+ PRINTABLE, /* M */
+ PRINTABLE, /* N */
+ PRINTABLE, /* O */
+ PRINTABLE, /* P */
+ PRINTABLE, /* Q */
+ PRINTABLE, /* R */
+ PRINTABLE, /* S */
+ PRINTABLE, /* T */
+ PRINTABLE, /* U */
+ PRINTABLE, /* V */
+ PRINTABLE, /* W */
+ PRINTABLE, /* X */
+ PRINTABLE, /* Y */
+ PRINTABLE, /* Z */
+ PRINTABLE, /* [ */
+ PRINTABLE|BACKSLASH, /* \ */
+ PRINTABLE, /* ] */
+ PRINTABLE, /* ^ */
+ PRINTABLE, /* _ */
+ PRINTABLE, /* ` */
+ PRINTABLE, /* a */
+ PRINTABLE, /* b */
+ PRINTABLE, /* c */
+ PRINTABLE, /* d */
+ PRINTABLE, /* e */
+ PRINTABLE, /* f */
+ PRINTABLE, /* g */
+ PRINTABLE, /* h */
+ PRINTABLE, /* i */
+ PRINTABLE, /* j */
+ PRINTABLE, /* k */
+ PRINTABLE, /* l */
+ PRINTABLE, /* m */
+ PRINTABLE, /* n */
+ PRINTABLE, /* o */
+ PRINTABLE, /* p */
+ PRINTABLE, /* q */
+ PRINTABLE, /* r */
+ PRINTABLE, /* s */
+ PRINTABLE, /* t */
+ PRINTABLE, /* u */
+ PRINTABLE, /* v */
+ PRINTABLE, /* w */
+ PRINTABLE, /* x */
+ PRINTABLE, /* y */
+ PRINTABLE, /* z */
+ PRINTABLE, /* { */
+ PRINTABLE|VBAR, /* | */
+ PRINTABLE, /* } */
+ PRINTABLE|TWIDDLE, /* ~ */
+ IGNORE, /* ^? */
+};
+
+static int lex_unget_c;
+
+static inline int
+lex_get()
+{
+ int c;
+ if (lex_unget_c) {
+ c = lex_unget_c;
+ lex_unget_c = 0;
+ } else {
+ c = ao_lisp_getc();
+ }
+ return c;
+}
+
+static inline void
+lex_unget(int c)
+{
+ if (c != EOF)
+ lex_unget_c = c;
+}
+
+static int
+lex_quoted (void)
+{
+ int c;
+ int v;
+ int count;
+
+ c = lex_get();
+ if (c == EOF)
+ return EOF;
+ c &= 0x7f;
+ switch (c) {
+ case 'n':
+ return '\n';
+ case 'f':
+ return '\f';
+ case 'b':
+ return '\b';
+ case 'r':
+ return '\r';
+ case 'v':
+ return '\v';
+ case 't':
+ return '\t';
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ v = c - '0';
+ count = 1;
+ while (count <= 3) {
+ c = lex_get();
+ if (c == EOF)
+ return EOF;
+ c &= 0x7f;
+ if (c < '0' || '7' < c) {
+ lex_unget(c);
+ break;
+ }
+ v = (v << 3) + c - '0';
+ ++count;
+ }
+ return v;
+ default:
+ return c;
+ }
+}
+
+static uint16_t lex_class;
+
+static int
+lexc(void)
+{
+ int c;
+ do {
+ c = lex_get();
+ if (c == EOF) {
+ lex_class = ENDOFFILE;
+ c = 0;
+ } else {
+ c &= 0x7f;
+ lex_class = lex_classes[c];
+ if (lex_class & BACKSLASH) {
+ c = lex_quoted();
+ if (c == EOF)
+ lex_class = ENDOFFILE;
+ else
+ lex_class = PRINTABLE;
+ }
+ }
+ } while (lex_class & IGNORE);
+ return c;
+}
+
+#define AO_LISP_TOKEN_MAX 32
+
+static char token_string[AO_LISP_TOKEN_MAX];
+static int token_int;
+static int token_len;
+
+static inline void add_token(int c) {
+ if (c && token_len < AO_LISP_TOKEN_MAX - 1)
+ token_string[token_len++] = c;
+}
+
+static inline void end_token(void) {
+ token_string[token_len] = '\0';
+}
+
+static int
+lex(void)
+{
+ int c;
+
+ token_len = 0;
+ for (;;) {
+ c = lexc();
+ if (lex_class & ENDOFFILE)
+ return END;
+
+ if (lex_class & WHITE)
+ continue;
+
+ if (lex_class & COMMENT) {
+ while ((c = lexc()) != '\n') {
+ if (lex_class & ENDOFFILE)
+ return END;
+ }
+ continue;
+ }
+
+ if (lex_class & (BRA|KET|QUOTEC)) {
+ add_token(c);
+ end_token();
+ switch (c) {
+ case '(':
+ return OPEN;
+ case ')':
+ return CLOSE;
+ case '\'':
+ return QUOTE;
+ }
+ }
+ if (lex_class & TWIDDLE) {
+ token_int = lexc();
+ return NUM;
+ }
+ if (lex_class & STRINGC) {
+ for (;;) {
+ c = lexc();
+ if (lex_class & (STRINGC|ENDOFFILE)) {
+ end_token();
+ return STRING;
+ }
+ add_token(c);
+ }
+ }
+ if (lex_class & PRINTABLE) {
+ int isnum;
+ int hasdigit;
+ int isneg;
+
+ isnum = 1;
+ hasdigit = 0;
+ token_int = 0;
+ isneg = 0;
+ for (;;) {
+ if (!(lex_class & NUMBER)) {
+ isnum = 0;
+ } else {
+ if (token_len != 0 &&
+ (lex_class & SIGN))
+ {
+ isnum = 0;
+ }
+ if (c == '-')
+ isneg = 1;
+ if (lex_class & DIGIT) {
+ hasdigit = 1;
+ if (isnum)
+ token_int = token_int * 10 + c - '0';
+ }
+ }
+ add_token (c);
+ c = lexc ();
+ if (lex_class & (NOTNAME)) {
+// if (lex_class & ENDOFFILE)
+// clearerr (f);
+ lex_unget(c);
+ end_token ();
+ if (isnum && hasdigit) {
+ if (isneg)
+ token_int = -token_int;
+ return NUM;
+ }
+ return NAME;
+ }
+ }
+
+ }
+ }
+}
+
+static int parse_token;
+
+struct ao_lisp_cons *ao_lisp_read_cons;
+struct ao_lisp_cons *ao_lisp_read_cons_tail;
+struct ao_lisp_cons *ao_lisp_read_stack;
+
+static int
+push_read_stack(int cons, int in_quote)
+{
+ DBGI("push read stack %p %d\n", ao_lisp_read_cons, in_quote);
+ DBG_IN();
+ if (cons) {
+ ao_lisp_read_stack = ao_lisp_cons_cons(ao_lisp_cons_poly(ao_lisp_read_cons),
+ ao_lisp_cons_cons(ao_lisp_int_poly(in_quote),
+ ao_lisp_read_stack));
+ if (!ao_lisp_read_stack)
+ return 0;
+ }
+ ao_lisp_read_cons = NULL;
+ ao_lisp_read_cons_tail = NULL;
+ return 1;
+}
+
+static int
+pop_read_stack(int cons)
+{
+ int in_quote = 0;
+ if (cons) {
+ ao_lisp_read_cons = ao_lisp_poly_cons(ao_lisp_read_stack->car);
+ ao_lisp_read_stack = ao_lisp_poly_cons(ao_lisp_read_stack->cdr);
+ in_quote = ao_lisp_poly_int(ao_lisp_read_stack->car);
+ ao_lisp_read_stack = ao_lisp_poly_cons(ao_lisp_read_stack->cdr);
+ for (ao_lisp_read_cons_tail = ao_lisp_read_cons;
+ ao_lisp_read_cons_tail && ao_lisp_read_cons_tail->cdr;
+ ao_lisp_read_cons_tail = ao_lisp_poly_cons(ao_lisp_read_cons_tail->cdr))
+ ;
+ } else {
+ ao_lisp_read_cons = 0;
+ ao_lisp_read_cons_tail = 0;
+ ao_lisp_read_stack = 0;
+ }
+ DBG_OUT();
+ DBGI("pop read stack %p %d\n", ao_lisp_read_cons, in_quote);
+ return in_quote;
+}
+
+ao_poly
+ao_lisp_read(void)
+{
+ struct ao_lisp_atom *atom;
+ char *string;
+ int cons;
+ int in_quote;
+ ao_poly v;
+
+ parse_token = lex();
+ DBGI("token %d (%s)\n", parse_token, token_string);
+
+ cons = 0;
+ in_quote = 0;
+ ao_lisp_read_cons = ao_lisp_read_cons_tail = ao_lisp_read_stack = 0;
+ for (;;) {
+ while (parse_token == OPEN) {
+ if (!push_read_stack(cons, in_quote))
+ return AO_LISP_NIL;
+ cons++;
+ in_quote = 0;
+ parse_token = lex();
+ DBGI("token %d (%s)\n", parse_token, token_string);
+ }
+
+ switch (parse_token) {
+ case END:
+ default:
+ if (cons)
+ ao_lisp_error(AO_LISP_EOF, "unexpected end of file");
+ return _ao_lisp_atom_eof;
+ break;
+ case NAME:
+ atom = ao_lisp_atom_intern(token_string);
+ if (atom)
+ v = ao_lisp_atom_poly(atom);
+ else
+ v = AO_LISP_NIL;
+ break;
+ case NUM:
+ v = ao_lisp_int_poly(token_int);
+ break;
+ case STRING:
+ string = ao_lisp_string_copy(token_string);
+ if (string)
+ v = ao_lisp_string_poly(string);
+ else
+ v = AO_LISP_NIL;
+ break;
+ case QUOTE:
+ if (!push_read_stack(cons, in_quote))
+ return AO_LISP_NIL;
+ cons++;
+ in_quote = 1;
+ v = _ao_lisp_atom_quote;
+ break;
+ case CLOSE:
+ if (!cons) {
+ v = AO_LISP_NIL;
+ break;
+ }
+ v = ao_lisp_cons_poly(ao_lisp_read_cons);
+ --cons;
+ in_quote = pop_read_stack(cons);
+ break;
+ }
+
+ /* loop over QUOTE ends */
+ for (;;) {
+ if (!cons)
+ return v;
+
+ struct ao_lisp_cons *read = ao_lisp_cons_cons(v, NULL);
+ if (!read)
+ return AO_LISP_NIL;
+
+ if (ao_lisp_read_cons_tail)
+ ao_lisp_read_cons_tail->cdr = ao_lisp_cons_poly(read);
+ else
+ ao_lisp_read_cons = read;
+ ao_lisp_read_cons_tail = read;
+
+ if (!in_quote || !ao_lisp_read_cons->cdr)
+ break;
+
+ v = ao_lisp_cons_poly(ao_lisp_read_cons);
+ --cons;
+ in_quote = pop_read_stack(cons);
+ }
+
+ parse_token = lex();
+ DBGI("token %d (%s)\n", parse_token, token_string);
+ }
+ return v;
+}
diff --git a/src/lisp/ao_lisp_read.h b/src/lisp/ao_lisp_read.h
new file mode 100644
index 00000000..1c994d56
--- /dev/null
+++ b/src/lisp/ao_lisp_read.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_LISP_READ_H_
+#define _AO_LISP_READ_H_
+
+# define END 0
+# define NAME 1
+# define OPEN 2
+# define CLOSE 3
+# define QUOTE 4
+# define STRING 5
+# define NUM 6
+
+/*
+ * character classes
+ */
+
+# define PRINTABLE 0x00000001 /* \t \n ' ' - '~' */
+# define QUOTED 0x00000002 /* \ anything */
+# define BRA 0x00000004 /* ( [ { */
+# define KET 0x00000008 /* ) ] } */
+# define WHITE 0x00000010 /* ' ' \t \n */
+# define DIGIT 0x00000020 /* [0-9] */
+# define SIGN 0x00000040 /* +- */
+# define ENDOFFILE 0x00000080 /* end of file */
+# define COMMENT 0x00000100 /* ; # */
+# define IGNORE 0x00000200 /* \0 - ' ' */
+# define QUOTEC 0x00000400 /* ' */
+# define BACKSLASH 0x00000800 /* \ */
+# define VBAR 0x00001000 /* | */
+# define TWIDDLE 0x00002000 /* ~ */
+# define STRINGC 0x00004000 /* " */
+
+# define NOTNAME (STRINGC|TWIDDLE|VBAR|QUOTEC|COMMENT|ENDOFFILE|WHITE|KET|BRA)
+# define NUMBER (DIGIT|SIGN)
+
+#endif /* _AO_LISP_READ_H_ */
diff --git a/src/lisp/ao_lisp_rep.c b/src/lisp/ao_lisp_rep.c
new file mode 100644
index 00000000..3be95d44
--- /dev/null
+++ b/src/lisp/ao_lisp_rep.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+ao_poly
+ao_lisp_read_eval_print(void)
+{
+ ao_poly in, out = AO_LISP_NIL;
+ for(;;) {
+ in = ao_lisp_read();
+ if (in == _ao_lisp_atom_eof || in == AO_LISP_NIL)
+ break;
+ out = ao_lisp_eval(in);
+ if (ao_lisp_exception) {
+ ao_lisp_exception = 0;
+ } else {
+ ao_lisp_poly_print(out);
+ putchar ('\n');
+ }
+ }
+ return out;
+}
diff --git a/src/lisp/ao_lisp_save.c b/src/lisp/ao_lisp_save.c
new file mode 100644
index 00000000..4f850fb9
--- /dev/null
+++ b/src/lisp/ao_lisp_save.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao_lisp.h>
+
+ao_poly
+ao_lisp_save(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_save, cons, 0, 0))
+ return AO_LISP_NIL;
+
+#ifdef AO_LISP_SAVE
+ struct ao_lisp_os_save *os = (struct ao_lisp_os_save *) (void *) &ao_lisp_pool[AO_LISP_POOL];
+
+ ao_lisp_collect(AO_LISP_COLLECT_FULL);
+ os->atoms = ao_lisp_atom_poly(ao_lisp_atoms);
+ os->globals = ao_lisp_frame_poly(ao_lisp_frame_global);
+ os->const_checksum = ao_lisp_const_checksum;
+ os->const_checksum_inv = (uint16_t) ~ao_lisp_const_checksum;
+
+ if (ao_lisp_os_save())
+ return _ao_lisp_atom_t;
+#endif
+ return AO_LISP_NIL;
+}
+
+ao_poly
+ao_lisp_restore(struct ao_lisp_cons *cons)
+{
+ if (!ao_lisp_check_argc(_ao_lisp_atom_save, cons, 0, 0))
+ return AO_LISP_NIL;
+
+#ifdef AO_LISP_SAVE
+ struct ao_lisp_os_save save;
+ struct ao_lisp_os_save *os = (struct ao_lisp_os_save *) (void *) &ao_lisp_pool[AO_LISP_POOL];
+
+ if (!ao_lisp_os_restore_save(&save, AO_LISP_POOL))
+ return ao_lisp_error(AO_LISP_INVALID, "header restore failed");
+
+ if (save.const_checksum != ao_lisp_const_checksum ||
+ save.const_checksum_inv != (uint16_t) ~ao_lisp_const_checksum)
+ {
+ return ao_lisp_error(AO_LISP_INVALID, "image is corrupted or stale");
+ }
+
+ if (ao_lisp_os_restore()) {
+
+ ao_lisp_atoms = ao_lisp_poly_atom(os->atoms);
+ ao_lisp_frame_global = ao_lisp_poly_frame(os->globals);
+
+ /* Clear the eval global variabls */
+ ao_lisp_eval_clear_globals();
+
+ /* Reset the allocator */
+ ao_lisp_top = AO_LISP_POOL;
+ ao_lisp_collect(AO_LISP_COLLECT_FULL);
+
+ /* Re-create the evaluator stack */
+ if (!ao_lisp_eval_restart())
+ return AO_LISP_NIL;
+ return _ao_lisp_atom_t;
+ }
+#endif
+ return AO_LISP_NIL;
+}
diff --git a/src/lisp/ao_lisp_stack.c b/src/lisp/ao_lisp_stack.c
new file mode 100644
index 00000000..53adf432
--- /dev/null
+++ b/src/lisp/ao_lisp_stack.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+
+const struct ao_lisp_type ao_lisp_stack_type;
+
+static int
+stack_size(void *addr)
+{
+ (void) addr;
+ return sizeof (struct ao_lisp_stack);
+}
+
+static void
+stack_mark(void *addr)
+{
+ struct ao_lisp_stack *stack = addr;
+ for (;;) {
+ ao_lisp_poly_mark(stack->sexprs, 0);
+ ao_lisp_poly_mark(stack->values, 0);
+ /* no need to mark values_tail */
+ ao_lisp_poly_mark(stack->frame, 0);
+ ao_lisp_poly_mark(stack->list, 0);
+ stack = ao_lisp_poly_stack(stack->prev);
+ if (ao_lisp_mark_memory(&ao_lisp_stack_type, stack))
+ break;
+ }
+}
+
+static void
+stack_move(void *addr)
+{
+ struct ao_lisp_stack *stack = addr;
+
+ while (stack) {
+ struct ao_lisp_stack *prev;
+ int ret;
+ (void) ao_lisp_poly_move(&stack->sexprs, 0);
+ (void) ao_lisp_poly_move(&stack->values, 0);
+ (void) ao_lisp_poly_move(&stack->values_tail, 0);
+ (void) ao_lisp_poly_move(&stack->frame, 0);
+ (void) ao_lisp_poly_move(&stack->list, 0);
+ prev = ao_lisp_poly_stack(stack->prev);
+ if (!prev)
+ break;
+ ret = ao_lisp_move_memory(&ao_lisp_stack_type, (void **) &prev);
+ if (prev != ao_lisp_poly_stack(stack->prev))
+ stack->prev = ao_lisp_stack_poly(prev);
+ if (ret)
+ break;
+ stack = prev;
+ }
+}
+
+const struct ao_lisp_type ao_lisp_stack_type = {
+ .size = stack_size,
+ .mark = stack_mark,
+ .move = stack_move,
+ .name = "stack"
+};
+
+struct ao_lisp_stack *ao_lisp_stack_free_list;
+
+void
+ao_lisp_stack_reset(struct ao_lisp_stack *stack)
+{
+ stack->state = eval_sexpr;
+ stack->sexprs = AO_LISP_NIL;
+ stack->values = AO_LISP_NIL;
+ stack->values_tail = AO_LISP_NIL;
+}
+
+static struct ao_lisp_stack *
+ao_lisp_stack_new(void)
+{
+ struct ao_lisp_stack *stack;
+
+ if (ao_lisp_stack_free_list) {
+ stack = ao_lisp_stack_free_list;
+ ao_lisp_stack_free_list = ao_lisp_poly_stack(stack->prev);
+ } else {
+ stack = ao_lisp_alloc(sizeof (struct ao_lisp_stack));
+ if (!stack)
+ return 0;
+ stack->type = AO_LISP_STACK;
+ }
+ ao_lisp_stack_reset(stack);
+ return stack;
+}
+
+int
+ao_lisp_stack_push(void)
+{
+ struct ao_lisp_stack *stack = ao_lisp_stack_new();
+
+ if (!stack)
+ return 0;
+
+ stack->prev = ao_lisp_stack_poly(ao_lisp_stack);
+ stack->frame = ao_lisp_frame_poly(ao_lisp_frame_current);
+ stack->list = AO_LISP_NIL;
+
+ ao_lisp_stack = stack;
+
+ DBGI("stack push\n");
+ DBG_FRAMES();
+ DBG_IN();
+ return 1;
+}
+
+void
+ao_lisp_stack_pop(void)
+{
+ ao_poly prev;
+ struct ao_lisp_frame *prev_frame;
+
+ if (!ao_lisp_stack)
+ return;
+ prev = ao_lisp_stack->prev;
+ if (!ao_lisp_stack_marked(ao_lisp_stack)) {
+ ao_lisp_stack->prev = ao_lisp_stack_poly(ao_lisp_stack_free_list);
+ ao_lisp_stack_free_list = ao_lisp_stack;
+ }
+
+ ao_lisp_stack = ao_lisp_poly_stack(prev);
+ prev_frame = ao_lisp_frame_current;
+ if (ao_lisp_stack)
+ ao_lisp_frame_current = ao_lisp_poly_frame(ao_lisp_stack->frame);
+ else
+ ao_lisp_frame_current = NULL;
+ if (ao_lisp_frame_current != prev_frame)
+ ao_lisp_frame_free(prev_frame);
+ DBG_OUT();
+ DBGI("stack pop\n");
+ DBG_FRAMES();
+}
+
+void
+ao_lisp_stack_clear(void)
+{
+ ao_lisp_stack = NULL;
+ ao_lisp_frame_current = NULL;
+ ao_lisp_v = AO_LISP_NIL;
+}
+
+void
+ao_lisp_stack_print(ao_poly poly)
+{
+ struct ao_lisp_stack *s = ao_lisp_poly_stack(poly);
+
+ while (s) {
+ if (s->type & AO_LISP_STACK_PRINT) {
+ printf("[recurse...]");
+ return;
+ }
+ s->type |= AO_LISP_STACK_PRINT;
+ printf("\t[\n");
+ printf("\t\texpr: "); ao_lisp_poly_print(s->list); printf("\n");
+ printf("\t\tstate: %s\n", ao_lisp_state_names[s->state]);
+ ao_lisp_error_poly ("values: ", s->values, s->values_tail);
+ ao_lisp_error_poly ("sexprs: ", s->sexprs, AO_LISP_NIL);
+ ao_lisp_error_frame(2, "frame: ", ao_lisp_poly_frame(s->frame));
+ printf("\t]\n");
+ s->type &= ~AO_LISP_STACK_PRINT;
+ s = ao_lisp_poly_stack(s->prev);
+ }
+}
+
+/*
+ * Copy a stack, being careful to keep everybody referenced
+ */
+static struct ao_lisp_stack *
+ao_lisp_stack_copy(struct ao_lisp_stack *old)
+{
+ struct ao_lisp_stack *new = NULL;
+ struct ao_lisp_stack *n, *prev = NULL;
+
+ while (old) {
+ ao_lisp_stack_stash(0, old);
+ ao_lisp_stack_stash(1, new);
+ ao_lisp_stack_stash(2, prev);
+ n = ao_lisp_stack_new();
+ prev = ao_lisp_stack_fetch(2);
+ new = ao_lisp_stack_fetch(1);
+ old = ao_lisp_stack_fetch(0);
+ if (!n)
+ return NULL;
+
+ ao_lisp_stack_mark(old);
+ ao_lisp_frame_mark(ao_lisp_poly_frame(old->frame));
+ *n = *old;
+
+ if (prev)
+ prev->prev = ao_lisp_stack_poly(n);
+ else
+ new = n;
+ prev = n;
+
+ old = ao_lisp_poly_stack(old->prev);
+ }
+ return new;
+}
+
+/*
+ * Evaluate a continuation invocation
+ */
+ao_poly
+ao_lisp_stack_eval(void)
+{
+ struct ao_lisp_stack *new = ao_lisp_stack_copy(ao_lisp_poly_stack(ao_lisp_v));
+ if (!new)
+ return AO_LISP_NIL;
+
+ struct ao_lisp_cons *cons = ao_lisp_poly_cons(ao_lisp_stack->values);
+
+ if (!cons || !cons->cdr)
+ return ao_lisp_error(AO_LISP_INVALID, "continuation requires a value");
+
+ new->state = eval_val;
+
+ ao_lisp_stack = new;
+ ao_lisp_frame_current = ao_lisp_poly_frame(ao_lisp_stack->frame);
+
+ return ao_lisp_poly_cons(cons->cdr)->car;
+}
+
+/*
+ * Call with current continuation. This calls a lambda, passing
+ * it a single argument which is the current continuation
+ */
+ao_poly
+ao_lisp_call_cc(struct ao_lisp_cons *cons)
+{
+ struct ao_lisp_stack *new;
+ ao_poly v;
+
+ /* Make sure the single parameter is a lambda */
+ if (!ao_lisp_check_argc(_ao_lisp_atom_call2fcc, cons, 1, 1))
+ return AO_LISP_NIL;
+ if (!ao_lisp_check_argt(_ao_lisp_atom_call2fcc, cons, 0, AO_LISP_LAMBDA, 0))
+ return AO_LISP_NIL;
+
+ /* go get the lambda */
+ ao_lisp_v = ao_lisp_arg(cons, 0);
+
+ /* Note that the whole call chain now has
+ * a reference to it which may escape
+ */
+ new = ao_lisp_stack_copy(ao_lisp_stack);
+ if (!new)
+ return AO_LISP_NIL;
+
+ /* re-fetch cons after the allocation */
+ cons = ao_lisp_poly_cons(ao_lisp_poly_cons(ao_lisp_stack->values)->cdr);
+
+ /* Reset the arg list to the current stack,
+ * and call the lambda
+ */
+
+ cons->car = ao_lisp_stack_poly(new);
+ cons->cdr = AO_LISP_NIL;
+ v = ao_lisp_lambda_eval();
+ ao_lisp_stack->sexprs = v;
+ ao_lisp_stack->state = eval_progn;
+ return AO_LISP_NIL;
+}
diff --git a/src/lisp/ao_lisp_string.c b/src/lisp/ao_lisp_string.c
new file mode 100644
index 00000000..cd7b27a9
--- /dev/null
+++ b/src/lisp/ao_lisp_string.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_lisp.h"
+
+static void string_mark(void *addr)
+{
+ (void) addr;
+}
+
+static int string_size(void *addr)
+{
+ if (!addr)
+ return 0;
+ return strlen(addr) + 1;
+}
+
+static void string_move(void *addr)
+{
+ (void) addr;
+}
+
+const struct ao_lisp_type ao_lisp_string_type = {
+ .mark = string_mark,
+ .size = string_size,
+ .move = string_move,
+ .name = "string",
+};
+
+char *
+ao_lisp_string_copy(char *a)
+{
+ int alen = strlen(a);
+
+ ao_lisp_string_stash(0, a);
+ char *r = ao_lisp_alloc(alen + 1);
+ a = ao_lisp_string_fetch(0);
+ if (!r)
+ return NULL;
+ strcpy(r, a);
+ return r;
+}
+
+char *
+ao_lisp_string_cat(char *a, char *b)
+{
+ int alen = strlen(a);
+ int blen = strlen(b);
+
+ ao_lisp_string_stash(0, a);
+ ao_lisp_string_stash(1, b);
+ char *r = ao_lisp_alloc(alen + blen + 1);
+ a = ao_lisp_string_fetch(0);
+ b = ao_lisp_string_fetch(1);
+ if (!r)
+ return NULL;
+ strcpy(r, a);
+ strcpy(r+alen, b);
+ return r;
+}
+
+ao_poly
+ao_lisp_string_pack(struct ao_lisp_cons *cons)
+{
+ int len = ao_lisp_cons_length(cons);
+ ao_lisp_cons_stash(0, cons);
+ char *r = ao_lisp_alloc(len + 1);
+ cons = ao_lisp_cons_fetch(0);
+ char *s = r;
+
+ while (cons) {
+ if (ao_lisp_poly_type(cons->car) != AO_LISP_INT)
+ return ao_lisp_error(AO_LISP_INVALID, "non-int passed to pack");
+ *s++ = ao_lisp_poly_int(cons->car);
+ cons = ao_lisp_poly_cons(cons->cdr);
+ }
+ *s++ = 0;
+ return ao_lisp_string_poly(r);
+}
+
+ao_poly
+ao_lisp_string_unpack(char *a)
+{
+ struct ao_lisp_cons *cons = NULL, *tail = NULL;
+ int c;
+ int i;
+
+ for (i = 0; (c = a[i]); i++) {
+ ao_lisp_cons_stash(0, cons);
+ ao_lisp_cons_stash(1, tail);
+ ao_lisp_string_stash(0, a);
+ struct ao_lisp_cons *n = ao_lisp_cons_cons(ao_lisp_int_poly(c), NULL);
+ a = ao_lisp_string_fetch(0);
+ cons = ao_lisp_cons_fetch(0);
+ tail = ao_lisp_cons_fetch(1);
+
+ if (!n) {
+ cons = NULL;
+ break;
+ }
+ if (tail)
+ tail->cdr = ao_lisp_cons_poly(n);
+ else
+ cons = n;
+ tail = n;
+ }
+ return ao_lisp_cons_poly(cons);
+}
+
+void
+ao_lisp_string_print(ao_poly p)
+{
+ char *s = ao_lisp_poly_string(p);
+ char c;
+
+ putchar('"');
+ while ((c = *s++)) {
+ switch (c) {
+ case '\n':
+ printf ("\\n");
+ break;
+ case '\r':
+ printf ("\\r");
+ break;
+ case '\t':
+ printf ("\\t");
+ break;
+ default:
+ putchar(c);
+ break;
+ }
+ }
+ putchar('"');
+}
+
+void
+ao_lisp_string_patom(ao_poly p)
+{
+ char *s = ao_lisp_poly_string(p);
+ char c;
+
+ while ((c = *s++))
+ putchar(c);
+}
diff --git a/src/lpc/Makefile-lpc.defs b/src/lpc/Makefile-lpc.defs
index bccea5bc..c4521620 100644
--- a/src/lpc/Makefile-lpc.defs
+++ b/src/lpc/Makefile-lpc.defs
@@ -4,7 +4,7 @@ endif
include $(TOPDIR)/Makedefs
-vpath % $(TOPDIR)/lpc:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR/aes):$(TOPDIR):$(TOPDIR)/math
+vpath % $(TOPDIR)/lpc:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR)/aes:$(TOPDIR):$(TOPDIR)/math
vpath make-altitude $(TOPDIR)/util
vpath make-kalman $(TOPDIR)/util
vpath kalman.5c $(TOPDIR)/kalman
@@ -26,9 +26,12 @@ endif
ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
CC=$(ARM_CC)
-WARN_FLAGS=-Wall -Wextra -Werror
+WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align
+
+AO_CFLAGS=-I. -I$(TOPDIR)/lpc -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \
+ -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math -I$(TOPDIR) \
+ $(PDCLIB_INCLUDES)
-AO_CFLAGS=-I. -I$(TOPDIR)/lpc -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math -I$(TOPDIR) $(PDCLIB_INCLUDES)
LPC_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m0 -mthumb\
-ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h
index 5fc0f680..15106dea 100644
--- a/src/lpc/ao_arch_funcs.h
+++ b/src/lpc/ao_arch_funcs.h
@@ -109,7 +109,7 @@ ao_arch_memory_barrier() {
static inline void
ao_arch_init_stack(struct ao_task *task, void *start)
{
- uint32_t *sp = (uint32_t *) (task->stack + AO_STACK_SIZE);
+ uint32_t *sp = (uint32_t *) (void *) (task->stack + AO_STACK_SIZE);
uint32_t a = (uint32_t) start;
int i;
diff --git a/src/nucleao-32/.gitignore b/src/nucleao-32/.gitignore
new file mode 100644
index 00000000..cb8f78e5
--- /dev/null
+++ b/src/nucleao-32/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+nucleo-32*
diff --git a/src/nucleao-32/Makefile b/src/nucleao-32/Makefile
new file mode 100644
index 00000000..69049982
--- /dev/null
+++ b/src/nucleao-32/Makefile
@@ -0,0 +1,93 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_lisp.h \
+ ao_lisp_const.h \
+ stm32f0.h \
+ Makefile
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_beep_stm.c \
+ ao_dma_stm.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_usb_stm.c \
+ ao_serial_stm.c \
+ ao_flash_stm.c \
+ ao_lisp_atom.c \
+ ao_lisp_builtin.c \
+ ao_lisp_cons.c \
+ ao_lisp_error.c \
+ ao_lisp_eval.c \
+ ao_lisp_frame.c \
+ ao_lisp_int.c \
+ ao_lisp_lambda.c \
+ ao_lisp_lex.c \
+ ao_lisp_mem.c \
+ ao_lisp_poly.c \
+ ao_lisp_read.c \
+ ao_lisp_rep.c \
+ ao_lisp_save.c \
+ ao_lisp_stack.c \
+ ao_lisp_string.c \
+ ao_lisp_os_save.c
+
+PRODUCT=Nucleo-32
+PRODUCT_DEF=-DNUCLEO
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -Os -g
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tload.ld
+
+PROGNAME=nucleo-32
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_nucleo.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+load: $(PROG)
+ stm-load $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/nucleao-32/ao_nucleo.c b/src/nucleao-32/ao_nucleo.c
new file mode 100644
index 00000000..6b4cbaae
--- /dev/null
+++ b/src/nucleao-32/ao_nucleo.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_lisp.h>
+#include <ao_beep.h>
+
+static void lisp_cmd() {
+ ao_lisp_read_eval_print();
+}
+
+static void beep() {
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+}
+
+static const struct ao_cmds blink_cmds[] = {
+ { lisp_cmd, "l\0Run lisp interpreter" },
+ { beep, "b\0Beep" },
+ { 0, 0 }
+};
+
+void main(void)
+{
+ ao_led_init(LEDS_AVAILABLE);
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+ ao_dma_init();
+ ao_usb_init();
+ ao_serial_init();
+ ao_beep_init();
+ ao_cmd_init();
+ ao_cmd_register(blink_cmds);
+ ao_start_scheduler();
+}
+
+
diff --git a/src/nucleao-32/ao_pins.h b/src/nucleao-32/ao_pins.h
new file mode 100644
index 00000000..cee4616f
--- /dev/null
+++ b/src/nucleao-32/ao_pins.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPBEN
+#define LED_PORT (&stm_gpiob)
+#define LED_PIN_GREEN 3
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define AO_LED_PANIC AO_LED_GREEN
+#define AO_CMD_LEN 128
+#define AO_LISP_POOL_TOTAL 3072
+#define AO_LISP_SAVE 1
+#define AO_STACK_SIZE 1024
+
+#define LEDS_AVAILABLE (AO_LED_GREEN)
+
+#define AO_POWER_MANAGEMENT 0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 0
+#define HAS_BEEP 1
+
+#define BEEPER_TIMER 2
+#define BEEPER_CHANNEL 4
+#define BEEPER_PORT (&stm_gpioa)
+#define BEEPER_PIN 3
+
+#define IS_FLASH_LOADER 0
+
+#define HAS_SERIAL_2 1
+#define SERIAL_2_PA2_PA15 1
+#define USE_SERIAL_2_FLOW 0
+#define USE_SERIAL_2_STDIN 1
+#define DELAY_SERIAL_2_STDIN 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/nucleao-32/flash-loader/.gitignore b/src/nucleao-32/flash-loader/.gitignore
new file mode 100644
index 00000000..cb8f78e5
--- /dev/null
+++ b/src/nucleao-32/flash-loader/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+nucleo-32*
diff --git a/src/nucleao-32/flash-loader/Makefile b/src/nucleao-32/flash-loader/Makefile
new file mode 100644
index 00000000..2392e998
--- /dev/null
+++ b/src/nucleao-32/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=nucleo-32
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/nucleao-32/flash-loader/ao_pins.h b/src/nucleao-32/flash-loader/ao_pins.h
new file mode 100644
index 00000000..8bdbdb1a
--- /dev/null
+++ b/src/nucleao-32/flash-loader/ao_pins.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin D3, which is PB0 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpiob
+#define AO_BOOT_APPLICATION_PIN 0
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/pnpservo-v1/Makefile b/src/pnpservo-v1/Makefile
new file mode 100644
index 00000000..8606b1ae
--- /dev/null
+++ b/src/pnpservo-v1/Makefile
@@ -0,0 +1,72 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ stm32f0.h \
+ Makefile
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_dma_stm.c \
+ ao_stdio.c \
+ ao_mutex.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_usb_stm.c \
+ ao_flash_stm.c
+
+PRODUCT=PNPservo-v1
+PRODUCT_DEF=-DPNPSERVO
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) -I. $(STMF0_CFLAGS) -Os -g
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tlambda.ld
+
+PROGNAME=pnpservo-v1
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_pnpservo.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) lambda.ld altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+load: $(PROG)
+ stm-load $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/pnpservo-v1/ao_pins.h b/src/pnpservo-v1/ao_pins.h
new file mode 100644
index 00000000..38f3d8e5
--- /dev/null
+++ b/src/pnpservo-v1/ao_pins.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPAEN
+#define LED_PORT (&stm_gpioa)
+#define LED_PIN_RED 9
+#define LED_PIN_GREEN 10
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define AO_LED_PANIC AO_LED_RED
+#define AO_CMD_LEN 128
+#define AO_LISP_POOL_TOTAL 3072
+#define AO_LISP_SAVE 1
+#define AO_STACK_SIZE 1024
+
+/* need HSI active to write to flash */
+#define AO_NEED_HSI 1
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define AO_POWER_MANAGEMENT 0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+#define HAS_BEEP 0
+
+#define IS_FLASH_LOADER 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/pnpservo-v1/ao_pnpservo.c b/src/pnpservo-v1/ao_pnpservo.c
new file mode 100644
index 00000000..d4c2d495
--- /dev/null
+++ b/src/pnpservo-v1/ao_pnpservo.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+
+static const struct ao_cmds blink_cmds[] = {
+// { lisp_cmd, "l\0Run lisp interpreter" },
+ { 0, 0 }
+};
+
+
+void main(void)
+{
+ ao_led_init(LEDS_AVAILABLE);
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+ ao_dma_init();
+ ao_usb_init();
+ ao_cmd_init();
+ ao_cmd_register(blink_cmds);
+ ao_start_scheduler();
+}
+
+
diff --git a/src/pnpservo-v1/flash-loader/.gitignore b/src/pnpservo-v1/flash-loader/.gitignore
new file mode 100644
index 00000000..86ebb7f2
--- /dev/null
+++ b/src/pnpservo-v1/flash-loader/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+lambdakey*
diff --git a/src/pnpservo-v1/flash-loader/Makefile b/src/pnpservo-v1/flash-loader/Makefile
new file mode 100644
index 00000000..3283380c
--- /dev/null
+++ b/src/pnpservo-v1/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=pnpservo-v1
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/pnpservo-v1/flash-loader/ao_pins.h b/src/pnpservo-v1/flash-loader/ao_pins.h
new file mode 100644
index 00000000..4b788f67
--- /dev/null
+++ b/src/pnpservo-v1/flash-loader/ao_pins.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin 5 on debug connector */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 15
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/pnpservo-v1/lambda.ld b/src/pnpservo-v1/lambda.ld
new file mode 100644
index 00000000..5de65eb5
--- /dev/null
+++ b/src/pnpservo-v1/lambda.ld
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08001000, LENGTH = 25K
+ flash (r): ORIGIN = 0x08007400, LENGTH = 3k
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 6k - 128
+ stack (!w) : ORIGIN = 0x20000000 + 6k - 128, LENGTH = 128
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .interrupt ORIGIN(ram) : AT (ORIGIN(rom)) {
+ __interrupt_start__ = .;
+ __interrupt_rom__ = ORIGIN(rom);
+ *(.interrupt) /* Interrupt vectors */
+ __interrupt_end__ = .;
+ } > ram
+
+ .text ORIGIN(rom) + 0x100 : {
+ __text_start__ = .;
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+
+ *(.text*) /* Executable code */
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ *(.rodata*) /* Constants */
+
+ } > rom
+ __text_end__ = .;
+
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ . = ALIGN(4);
+ __boot_end__ = .;
+ } >ram
+
+ /* Functions placed in RAM (required for flashing)
+ *
+ * Align to 8 bytes as that's what the ARM likes text
+ * segment alignments to be, and if we don't, then
+ * we end up with a mismatch between the location in
+ * ROM and the desired location in RAM. I don't
+ * entirely understand this, but at least this appears
+ * to work...
+ */
+
+ .textram BLOCK(8): {
+ __data_start__ = .;
+ __text_ram_start__ = .;
+ *(.ramtext)
+ __text_ram_end = .;
+ } >ram AT>rom
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+
+ __flash__ = ORIGIN(flash);
+}
+
+ENTRY(start);
diff --git a/src/stm-vga/Makefile b/src/stm-vga/Makefile
new file mode 100644
index 00000000..46a77272
--- /dev/null
+++ b/src/stm-vga/Makefile
@@ -0,0 +1,83 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_vga.h \
+ ao_draw.h \
+ ao_draw_int.h \
+ ao_font.h \
+ ao_ps2.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_boot_chain.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_lcd_stm.c \
+ ao_lcd_font.c \
+ ao_vga.c \
+ ao_blt.c \
+ ao_copy.c \
+ ao_rect.c \
+ ao_text.c \
+ ao_line.c \
+ ao_mutex.c \
+ ao_dma_stm.c \
+ ao_adc_stm.c \
+ ao_data.c \
+ ao_i2c_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_ps2.c \
+ ao_console.c
+
+PRODUCT=StmVga-v0.0
+IDPRODUCT=0x000a
+
+CFLAGS = $(STM_CFLAGS) -g -Os
+
+PROG=stm-vga-$(VERSION)
+ELF=$(PROG).elf
+IHX=$(PROG).ihx
+
+SRC=$(ALTOS_SRC) ao_demo.c
+OBJ=$(SRC:.c=.o)
+
+all: $(ELF) $(IHX)
+
+$(ELF): Makefile $(OBJ)
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+distclean: clean
+
+clean:
+ rm -f *.o *.elf *.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm-vga/ao_demo.c b/src/stm-vga/ao_demo.c
new file mode 100644
index 00000000..1b443b1f
--- /dev/null
+++ b/src/stm-vga/ao_demo.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_event.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+#include <ao_boot.h>
+#include <ao_vga.h>
+#include <ao_ps2.h>
+#include <ao_console.h>
+
+struct ao_task ball_task;
+struct ao_task ps2_task;
+
+#define BALL_WIDTH 5
+#define BALL_HEIGHT 5
+
+static int ball_x;
+static int ball_y;
+static int ball_dx, ball_dy;
+
+uint8_t ball_enable;
+
+void
+ao_ball(void)
+{
+ ball_dx = 1;
+ ball_dy = 1;
+ ball_x = 0;
+ ball_y = 0;
+ for (;;) {
+ while (!ball_enable)
+ ao_sleep(&ball_enable);
+ for (;;) {
+ ao_line(&ao_vga_bitmap,
+ -100, -100, ball_x*2, ball_y*2,
+ 1, AO_XOR);
+ ao_text(&ao_vga_bitmap,
+ ball_x, ball_y - 10,
+ "Hello, Bdale!",
+ 1, AO_XOR);
+ ao_rect(&ao_vga_bitmap,
+ ball_x, ball_y,
+ BALL_WIDTH,
+ BALL_HEIGHT,
+ 1,
+ AO_XOR);
+ ao_delay(AO_MS_TO_TICKS(10));
+ ao_rect(&ao_vga_bitmap,
+ ball_x, ball_y,
+ BALL_WIDTH,
+ BALL_HEIGHT,
+ 1,
+ AO_XOR);
+ ao_text(&ao_vga_bitmap,
+ ball_x, ball_y - 10,
+ "Hello, Bdale!",
+ 1, AO_XOR);
+ ao_line(&ao_vga_bitmap,
+ -100, -100, ball_x*2, ball_y*2,
+ 1, AO_XOR);
+ if (!ball_enable)
+ break;
+ ball_x += ball_dx;
+ ball_y += ball_dy;
+ if (ball_x + BALL_WIDTH > AO_VGA_WIDTH) {
+ ball_x = AO_VGA_WIDTH - BALL_WIDTH;
+ ball_dx = -ball_dx;
+ }
+ if (ball_x < 0) {
+ ball_x = -ball_x;
+ ball_dx = -ball_dx;
+ }
+ if (ball_y + BALL_HEIGHT > AO_VGA_HEIGHT) {
+ ball_y = AO_VGA_HEIGHT - BALL_HEIGHT;
+ ball_dy = -ball_dy;
+ }
+ if (ball_y < 0) {
+ ball_y = -ball_y;
+ ball_dy = -ball_dy;
+ }
+ }
+ }
+}
+
+void
+ao_ps2(void)
+{
+ uint8_t leds = 0;
+ for (;;) {
+ uint8_t b = ao_ps2_get();
+ printf ("%02x\n", b);
+ flush();
+ if (b == 0x14) {
+ leds ^= 4;
+ ao_ps2_put(0xed);
+ if (ao_ps2_get() == 0xfa)
+ ao_ps2_put(leds);
+ }
+ }
+}
+
+static void
+ao_fb_init(void)
+{
+ ao_rect(&ao_vga_bitmap,
+ 0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
+ 1, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ 10, 10, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ AO_VGA_WIDTH - 20, 10, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ 10, AO_VGA_HEIGHT - 20, 10, 10,
+ 0, AO_COPY);
+
+ ao_rect(&ao_vga_bitmap,
+ AO_VGA_WIDTH - 20, AO_VGA_HEIGHT - 20, 10, 10,
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 20, 100,
+ "Hello, Bdale!",
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 1, ao_font.ascent,
+ "UL",
+ 0, AO_COPY);
+
+ ao_text(&ao_vga_bitmap,
+ 1, AO_VGA_HEIGHT - ao_font.descent,
+ "BL",
+ 0, AO_COPY);
+}
+
+static void
+ao_video_toggle(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_lex_i)
+ ao_fb_init();
+ ao_vga_enable(ao_cmd_lex_i);
+}
+
+static void
+ao_ball_toggle(void)
+{
+ ao_cmd_decimal();
+ ball_enable = ao_cmd_lex_i;
+ ao_wakeup(&ball_enable);
+}
+
+static void
+ao_ps2_read_keys(void)
+{
+ char c;
+
+ for (;;) {
+ c = ao_ps2_getchar();
+ printf("%02x %c\n", c, ' ' <= c && c < 0x7f ? c : '.');
+ flush();
+ if (c == ' ')
+ break;
+ }
+}
+
+static void
+ao_console_send(void)
+{
+ char c;
+
+ while ((c = getchar()) != '~') {
+ ao_console_putchar(c);
+ flush();
+ }
+}
+
+__code struct ao_cmds ao_demo_cmds[] = {
+ { ao_video_toggle, "V\0Toggle video" },
+ { ao_ball_toggle, "B\0Toggle ball" },
+ { ao_ps2_read_keys, "K\0Read keys from keyboard" },
+ { ao_console_send, "C\0Send data to console, end with ~" },
+ { 0, NULL }
+};
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_task_init();
+
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_GREEN);
+ ao_led_off(AO_LED_BLUE);
+ ao_timer_init();
+ ao_dma_init();
+ ao_cmd_init();
+ ao_vga_init();
+ ao_usb_init();
+ ao_exti_init();
+ ao_ps2_init();
+ ao_console_init();
+
+ ao_add_task(&ball_task, ao_ball, "ball");
+ ao_cmd_register(&ao_demo_cmds[0]);
+
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/stm-vga/ao_lisp_os.h b/src/stm-vga/ao_lisp_os.h
new file mode 100644
index 00000000..1993ac44
--- /dev/null
+++ b/src/stm-vga/ao_lisp_os.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LISP_OS_H_
+#define _AO_LISP_OS_H_
+
+#include "ao.h"
+
+static inline int
+ao_lisp_getc() {
+ static uint8_t at_eol;
+ int c;
+
+ if (at_eol) {
+ ao_cmd_readline();
+ at_eol = 0;
+ }
+ c = ao_cmd_lex();
+ if (c == '\n')
+ at_eol = 1;
+ return c;
+}
+
+static inline void
+ao_lisp_os_flush(void)
+{
+ flush();
+}
+
+static inline void
+ao_lisp_abort(void)
+{
+ ao_panic(1);
+}
+
+static inline void
+ao_lisp_os_led(int led)
+{
+ ao_led_set(led);
+}
+
+static inline void
+ao_lisp_os_delay(int delay)
+{
+ ao_delay(AO_MS_TO_TICKS(delay));
+}
+
+#endif
diff --git a/src/stm-vga/ao_lisp_os_save.c b/src/stm-vga/ao_lisp_os_save.c
new file mode 100644
index 00000000..7c853990
--- /dev/null
+++ b/src/stm-vga/ao_lisp_os_save.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_lisp.h>
+#include <ao_flash.h>
+
+extern uint8_t __flash__[];
+
+/* saved variables to rebuild the heap
+
+ ao_lisp_atoms
+ ao_lisp_frame_global
+ */
+
+int
+ao_lisp_os_save(void)
+{
+ int i;
+
+ for (i = 0; i < AO_LISP_POOL_TOTAL; i += 256) {
+ uint32_t *dst = (uint32_t *) (void *) &__flash__[i];
+ uint32_t *src = (uint32_t *) (void *) &ao_lisp_pool[i];
+
+ ao_flash_page(dst, src);
+ }
+ return 1;
+}
+
+int
+ao_lisp_os_restore_save(struct ao_lisp_os_save *save, int offset)
+{
+ memcpy(save, &__flash__[offset], sizeof (struct ao_lisp_os_save));
+ return 1;
+}
+
+int
+ao_lisp_os_restore(void)
+{
+ memcpy(ao_lisp_pool, __flash__, AO_LISP_POOL_TOTAL);
+ return 1;
+}
diff --git a/src/stm-vga/ao_pins.h b/src/stm-vga/ao_pins.h
new file mode 100644
index 00000000..8503c4fd
--- /dev/null
+++ b/src/stm-vga/ao_pins.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* Bridge SB17 on the board and use the MCO from the other chip */
+#define AO_HSE 8000000
+#define AO_HSE_BYPASS 1
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 24MHz */
+#define AO_PLLDIV 4
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_4)
+
+/* HCLK = 24MHZ (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at HCLK/1 */
+#define AO_APB1_PRESCALER 1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_1
+
+/* Run APB2 at HCLK/1 */
+#define AO_APB2_PRESCALER 1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+
+/* Allow for non-maskable interrupts at priority 0 */
+#define AO_NONMASK_INTERRUPT 1
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 1
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 0
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 1
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 1
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 1
+
+#define HAS_SPI_1 0
+#define SPI_1_PB3_PB4_PB5 1
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 0
+
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define PACKET_HAS_SLAVE 0
+#define HAS_TASK_QUEUE 1
+
+#define CONSOLE_STDIN 1
+
+#define AO_STACK_SIZE 1024
+
+#define STM_DMA1_3_STOLEN 1
+
+#define AO_BOOT_CHAIN 1
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOBEN
+#define LED_PORT (&stm_gpiob)
+#define LED_PIN_GREEN 7
+#define LED_PIN_BLUE 6
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define AO_LED_BLUE (1 << LED_PIN_BLUE)
+#define AO_LED_PANIC AO_LED_BLUE
+
+#define LEDS_AVAILABLE (AO_LED_BLUE | AO_LED_GREEN)
+
+#define AO_LCD_STM_SEG_ENABLED_0 ( \
+ (1 << 0) | /* PA1 */ \
+ (1 << 1) | /* PA2 */ \
+ (1 << 2) | /* PA3 */ \
+ (0 << 3) | /* PA6 */ \
+ (0 << 4) | /* PA7 */ \
+ (0 << 5) | /* PB0 */ \
+ (0 << 6) | /* PB1 */ \
+ (1 << 7) | /* PB3 */ \
+ (1 << 8) | /* PB4 */ \
+ (1 << 9) | /* PB5 */ \
+ (1 << 10) | /* PB10 */ \
+ (1 << 11) | /* PB11 */ \
+ (1 << 12) | /* PB12 */ \
+ (1 << 13) | /* PB13 */ \
+ (1 << 14) | /* PB14 */ \
+ (1 << 15) | /* PB15 */ \
+ (1 << 16) | /* PB8 */ \
+ (1 << 17) | /* PA15 */ \
+ (1 << 18) | /* PC0 */ \
+ (1 << 19) | /* PC1 */ \
+ (1 << 20) | /* PC2 */ \
+ (1 << 21) | /* PC3 */ \
+ (0 << 22) | /* PC4 */ \
+ (0 << 23) | /* PC5 */ \
+ (1 << 24) | /* PC6 */ \
+ (1 << 25) | /* PC7 */ \
+ (1 << 26) | /* PC8 */ \
+ (1 << 27) | /* PC9 */ \
+ (1 << 28) | /* PC10 or PD8 */ \
+ (1 << 29) | /* PC11 or PD9 */ \
+ (0 << 30) | /* PC12 or PD10 */ \
+ (0 << 31)) /* PD2 or PD11 */
+
+#define AO_LCD_STM_SEG_ENABLED_1 ( \
+ (0 << 0) | /* PD12 */ \
+ (0 << 1) | /* PD13 */ \
+ (0 << 2) | /* PD14 */ \
+ (0 << 3) | /* PD15 */ \
+ (0 << 4) | /* PE0 */ \
+ (0 << 5) | /* PE1 */ \
+ (0 << 6) | /* PE2 */ \
+ (0 << 7)) /* PE3 */
+
+#define AO_LCD_STM_COM_ENABLED ( \
+ (1 << 0) | /* PA8 */ \
+ (1 << 1) | /* PA9 */ \
+ (1 << 2) | /* PA10 */ \
+ (1 << 3) | /* PB9 */ \
+ (0 << 4) | /* PC10 */ \
+ (0 << 5) | /* PC11 */ \
+ (0 << 6)) /* PC12 */
+
+#define AO_LCD_28_ON_C 1
+
+#define AO_LCD_DUTY STM_LCD_CR_DUTY_STATIC
+
+#define HAS_ADC 1
+
+#define AO_ADC_RING 32
+
+struct ao_adc {
+ uint16_t tick;
+ int16_t idd;
+ int16_t temp;
+ int16_t vref;
+};
+
+#define AO_ADC_IDD 4
+#define AO_ADC_PIN0_PORT (&stm_gpioa)
+#define AO_ADC_PIN0_PIN 4
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN))
+#define AO_ADC_TEMP 16
+#define AO_ADC_VREF 17
+
+#define HAS_ADC_TEMP 1
+
+#define AO_DATA_RING 32
+#define AO_NUM_ADC 3
+
+#define AO_ADC_SQ1 AO_ADC_IDD
+#define AO_ADC_SQ2 AO_ADC_TEMP
+#define AO_ADC_SQ3 AO_ADC_VREF
+
+#define HAS_I2C_1 1
+#define I2C_1_PB6_PB7 0
+#define I2C_1_PB8_PB9 1
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 0
+
+#define AO_EVENT 1
+
+#define AO_QUADRATURE_COUNT 2
+#define AO_QUADRATURE_MODE AO_EXTI_MODE_PULL_UP
+
+#define AO_QUADRATURE_0_PORT &stm_gpioc
+#define AO_QUADRATURE_0_A 1
+#define AO_QUADRATURE_0_B 0
+
+#define AO_QUADRATURE_1_PORT &stm_gpioc
+#define AO_QUADRATURE_1_A 3
+#define AO_QUADRATURE_1_B 2
+
+#define AO_BUTTON_COUNT 2
+#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_0_PORT &stm_gpioc
+#define AO_BUTTON_0 6
+
+#define AO_BUTTON_1_PORT &stm_gpioc
+#define AO_BUTTON_1 7
+
+#define AO_TICK_TYPE uint32_t
+#define AO_TICK_SIGNED int32_t
+
+#define AO_PS2_CLOCK_PORT (&stm_gpioc)
+#define AO_PS2_CLOCK_BIT 0
+
+#define AO_PS2_DATA_PORT (&stm_gpioc)
+#define AO_PS2_DATA_BIT 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/stm/Makefile.defs b/src/stm/Makefile.defs
index 0ba86f5a..66ed4be8 100644
--- a/src/stm/Makefile.defs
+++ b/src/stm/Makefile.defs
@@ -1,4 +1,4 @@
-vpath % ../stm:../product:../drivers:../kernel:../util:../kalman:../aes:../math:..
+vpath % ../stm:../product:../drivers:../kernel:../util:../kalman:../aes:../math:../draw:../lisp:..
vpath make-altitude ../util
vpath make-kalman ../util
vpath kalman.5c ../kalman
@@ -26,7 +26,7 @@ LIBS=$(PDCLIB_LIBS_M3) -lgcc
WARN_FLAGS=-Wall -Wextra -Werror
-AO_CFLAGS=-I. -I../stm -I../kernel -I../drivers -I../math -I.. $(PDCLIB_INCLUDES)
+AO_CFLAGS=-I. -I../stm -I../kernel -I../drivers -I../math -I../draw -I../lisp -I.. $(PDCLIB_INCLUDES)
STM_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m3 -mthumb -Wcast-align \
-ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
diff --git a/src/stm/altos-512.ld b/src/stm/altos-512.ld
new file mode 100644
index 00000000..78c41685
--- /dev/null
+++ b/src/stm/altos-512.ld
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08001000, LENGTH = 508K
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 81408
+ stack (!w) : ORIGIN = 0x20013e00, LENGTH = 512
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .text ORIGIN(rom) : {
+ __text_start__ = .;
+ *(.interrupt) /* Interrupt vectors */
+
+ . = ORIGIN(rom) + 0x100;
+
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+ *(.text*) /* Executable code */
+ *(.rodata*) /* Constants */
+
+ } > rom
+
+ .ARM.exidx : {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } > rom
+ __text_end__ = .;
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ . = ALIGN(4);
+ __boot_end__ = .;
+ } >ram
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ __data_start__ = .;
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm/ao_arch.h b/src/stm/ao_arch.h
index 0cc29376..5f033b66 100644
--- a/src/stm/ao_arch.h
+++ b/src/stm/ao_arch.h
@@ -85,10 +85,6 @@ extern const uint32_t ao_radio_cal;
#define ao_arch_task_members\
uint32_t *sp; /* saved stack pointer */
-#define ao_arch_block_interrupts() asm("cpsid i")
-#define ao_arch_release_interrupts() asm("cpsie i")
-
-
/*
* For now, we're running at a weird frequency
*/
@@ -122,10 +118,21 @@ extern const uint32_t ao_radio_cal;
#define AO_TIM91011_CLK (2 * AO_PCLK2)
#endif
-#define AO_STM_NVIC_HIGH_PRIORITY 4
-#define AO_STM_NVIC_CLOCK_PRIORITY 6
-#define AO_STM_NVIC_MED_PRIORITY 8
-#define AO_STM_NVIC_LOW_PRIORITY 10
+/* The stm32l implements only 4 bits of the priority fields */
+
+#if AO_NONMASK_INTERRUPT
+#define AO_STM_NVIC_NONMASK_PRIORITY 0x00
+
+/* Set the basepri register to this value to mask all
+ * non-maskable priorities
+ */
+#define AO_STM_NVIC_BASEPRI_MASK 0x10
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY 0x40
+#define AO_STM_NVIC_MED_PRIORITY 0x80
+#define AO_STM_NVIC_LOW_PRIORITY 0xC0
+#define AO_STM_NVIC_CLOCK_PRIORITY 0xf0
void ao_lcd_stm_init(void);
diff --git a/src/stm/ao_arch_funcs.h b/src/stm/ao_arch_funcs.h
index a9d0fa34..522059bc 100644
--- a/src/stm/ao_arch_funcs.h
+++ b/src/stm/ao_arch_funcs.h
@@ -202,8 +202,11 @@ ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t s
#define ao_gpio_set_bits(port, bits) stm_gpio_set_bits(port, bits)
+#define ao_gpio_set_mask(port, bits, mask) stm_gpio_set_mask(port, bits, mask)
+
#define ao_gpio_clr_bits(port, bits) stm_gpio_clr_bits(port, bits);
+#define ao_gpio_get_all(port) stm_gpio_get_all(port)
#define ao_enable_output(port,bit,pin,v) do { \
ao_enable_port(port); \
@@ -211,6 +214,18 @@ ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t s
stm_moder_set(port, bit, STM_MODER_OUTPUT);\
} while (0)
+#define ao_enable_output_mask(port,bits,mask) do { \
+ ao_enable_port(port); \
+ ao_gpio_set_mask(port, bits, mask); \
+ ao_set_output_mask(port, mask); \
+ } while (0)
+
+#define AO_OUTPUT_PUSH_PULL STM_OTYPER_PUSH_PULL
+#define AO_OUTPUT_OPEN_DRAIN STM_OTYPER_OPEN_DRAIN
+
+#define ao_gpio_set_output_mode(port,bit,pin,mode) \
+ stm_otyper_set(port, pin, mode)
+
#define ao_gpio_set_mode(port,bit,mode) do { \
if (mode == AO_EXTI_MODE_PULL_UP) \
stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP); \
@@ -219,36 +234,73 @@ ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t s
else \
stm_pupdr_set(port, bit, STM_PUPDR_NONE); \
} while (0)
-
+
+#define ao_gpio_set_mode_mask(port,mask,mode) do { \
+ if (mode == AO_EXTI_MODE_PULL_UP) \
+ stm_pupdr_set_mask(port, mask, STM_PUPDR_PULL_UP); \
+ else if (mode == AO_EXTI_MODE_PULL_DOWN) \
+ stm_pupdr_set_mask(port, mask, STM_PUPDR_PULL_DOWN); \
+ else \
+ stm_pupdr_set_mask(port, mask, STM_PUPDR_NONE); \
+ } while (0)
+
+#define ao_set_input(port, bit) do { \
+ stm_moder_set(port, bit, STM_MODER_INPUT); \
+ } while (0)
+
+#define ao_set_output(port, bit, pin, v) do { \
+ ao_gpio_set(port, bit, pin, v); \
+ stm_moder_set(port, bit, STM_MODER_OUTPUT); \
+ } while (0)
+
+#define ao_set_output_mask(port, mask) do { \
+ stm_moder_set_mask(port, mask, STM_MODER_OUTPUT); \
+ } while (0)
+
+#define ao_set_input_mask(port, mask) do { \
+ stm_moder_set_mask(port, mask, STM_MODER_INPUT); \
+ } while (0)
+
#define ao_enable_input(port,bit,mode) do { \
ao_enable_port(port); \
- stm_moder_set(port, bit, STM_MODER_INPUT); \
+ ao_set_input(port, bit); \
ao_gpio_set_mode(port, bit, mode); \
} while (0)
-#define ao_enable_cs(port,bit) do { \
+#define ao_enable_input_mask(port,mask,mode) do { \
+ ao_enable_port(port); \
+ ao_gpio_set_mode_mask(port, mask, mode); \
+ ao_set_input_mask(port, mask); \
+ } while (0)
+
+#define _ao_enable_cs(port, bit) do { \
stm_gpio_set((port), bit, 1); \
stm_moder_set((port), bit, STM_MODER_OUTPUT); \
} while (0)
+#define ao_enable_cs(port,bit) do { \
+ ao_enable_port(port); \
+ _ao_enable_cs(port, bit); \
+ } while (0)
+
#define ao_spi_init_cs(port, mask) do { \
ao_enable_port(port); \
- if ((mask) & 0x0001) ao_enable_cs(port, 0); \
- if ((mask) & 0x0002) ao_enable_cs(port, 1); \
- if ((mask) & 0x0004) ao_enable_cs(port, 2); \
- if ((mask) & 0x0008) ao_enable_cs(port, 3); \
- if ((mask) & 0x0010) ao_enable_cs(port, 4); \
- if ((mask) & 0x0020) ao_enable_cs(port, 5); \
- if ((mask) & 0x0040) ao_enable_cs(port, 6); \
- if ((mask) & 0x0080) ao_enable_cs(port, 7); \
- if ((mask) & 0x0100) ao_enable_cs(port, 8); \
- if ((mask) & 0x0200) ao_enable_cs(port, 9); \
- if ((mask) & 0x0400) ao_enable_cs(port, 10);\
- if ((mask) & 0x0800) ao_enable_cs(port, 11);\
- if ((mask) & 0x1000) ao_enable_cs(port, 12);\
- if ((mask) & 0x2000) ao_enable_cs(port, 13);\
- if ((mask) & 0x4000) ao_enable_cs(port, 14);\
- if ((mask) & 0x8000) ao_enable_cs(port, 15);\
+ if ((mask) & 0x0001) _ao_enable_cs(port, 0); \
+ if ((mask) & 0x0002) _ao_enable_cs(port, 1); \
+ if ((mask) & 0x0004) _ao_enable_cs(port, 2); \
+ if ((mask) & 0x0008) _ao_enable_cs(port, 3); \
+ if ((mask) & 0x0010) _ao_enable_cs(port, 4); \
+ if ((mask) & 0x0020) _ao_enable_cs(port, 5); \
+ if ((mask) & 0x0040) _ao_enable_cs(port, 6); \
+ if ((mask) & 0x0080) _ao_enable_cs(port, 7); \
+ if ((mask) & 0x0100) _ao_enable_cs(port, 8); \
+ if ((mask) & 0x0200) _ao_enable_cs(port, 9); \
+ if ((mask) & 0x0400) _ao_enable_cs(port, 10);\
+ if ((mask) & 0x0800) _ao_enable_cs(port, 11);\
+ if ((mask) & 0x1000) _ao_enable_cs(port, 12);\
+ if ((mask) & 0x2000) _ao_enable_cs(port, 13);\
+ if ((mask) & 0x4000) _ao_enable_cs(port, 14);\
+ if ((mask) & 0x8000) _ao_enable_cs(port, 15);\
} while (0)
/* ao_dma_stm.c
@@ -345,17 +397,43 @@ extern struct ao_stm_usart ao_stm_usart3;
typedef uint32_t ao_arch_irq_t;
+static inline void
+ao_arch_block_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("cpsid i");
+#endif
+}
+
+static inline void
+ao_arch_release_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (0x0));
+#else
+ asm("cpsie i");
+#endif
+}
+
static inline uint32_t
ao_arch_irqsave(void) {
- uint32_t primask;
- asm("mrs %0,primask" : "=&r" (primask));
+ uint32_t val;
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("mrs %0,basepri" : "=r" (val));
+#else
+ asm("mrs %0,primask" : "=r" (val));
+#endif
ao_arch_block_interrupts();
- return primask;
+ return val;
}
static inline void
-ao_arch_irqrestore(uint32_t primask) {
- asm("msr primask,%0" : : "r" (primask));
+ao_arch_irqrestore(uint32_t basepri) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (basepri));
+#else
+ asm("msr primask,%0" : : "r" (basepri));
+#endif
}
static inline void
@@ -365,10 +443,17 @@ ao_arch_memory_barrier() {
static inline void
ao_arch_irq_check(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ uint32_t basepri;
+ asm("mrs %0,basepri" : "=r" (basepri));
+ if (basepri == 0)
+ ao_panic(AO_PANIC_IRQ);
+#else
uint32_t primask;
- asm("mrs %0,primask" : "=&r" (primask));
+ asm("mrs %0,primask" : "=r" (primask));
if ((primask & 1) == 0)
ao_panic(AO_PANIC_IRQ);
+#endif
}
#if HAS_TASK
@@ -390,7 +475,7 @@ ao_arch_init_stack(struct ao_task *task, void *start)
/* APSR */
ARM_PUSH32(sp, 0);
- /* PRIMASK with interrupts enabled */
+ /* BASEPRI with interrupts enabled */
ARM_PUSH32(sp, 0);
task->sp = sp;
@@ -404,8 +489,13 @@ static inline void ao_arch_save_regs(void) {
asm("mrs r0,apsr");
asm("push {r0}");
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Save BASEPRI */
+ asm("mrs r0,basepri");
+#else
/* Save PRIMASK */
asm("mrs r0,primask");
+#endif
asm("push {r0}");
}
@@ -419,9 +509,15 @@ static inline void ao_arch_restore_stack(void) {
/* Switch stacks */
asm("mov sp, %0" : : "r" (ao_cur_task->sp) );
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Restore BASEPRI */
+ asm("pop {r0}");
+ asm("msr basepri,r0");
+#else
/* Restore PRIMASK */
asm("pop {r0}");
asm("msr primask,r0");
+#endif
/* Restore APSR */
asm("pop {r0}");
@@ -463,7 +559,7 @@ static inline void ao_arch_start_scheduler(void) {
asm("mrs %0,msp" : "=&r" (sp));
asm("msr psp,%0" : : "r" (sp));
- asm("mrs %0,control" : "=&r" (control));
+ asm("mrs %0,control" : "=r" (control));
control |= (1 << 1);
asm("msr control,%0" : : "r" (control));
asm("isb");
@@ -474,12 +570,24 @@ static inline void ao_arch_start_scheduler(void) {
#endif
-#define ao_arch_wait_interrupt() do { \
- asm("\twfi\n"); \
- ao_arch_release_interrupts(); \
- asm(".global ao_idle_loc\nao_idle_loc:"); \
- ao_arch_block_interrupts(); \
- } while (0)
+static inline void
+ao_arch_wait_interrupt(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm(
+ "dsb\n" /* Serialize data */
+ "isb\n" /* Serialize instructions */
+ "cpsid i\n" /* Block all interrupts */
+ "msr basepri,%0\n" /* Allow all interrupts through basepri */
+ "wfi\n" /* Wait for an interrupt */
+ "cpsie i\n" /* Allow all interrupts */
+ "msr basepri,%1\n" /* Block interrupts through basepri */
+ : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("\twfi\n");
+ ao_arch_release_interrupts();
+ ao_arch_block_interrupts();
+#endif
+}
#define ao_arch_critical(b) do { \
uint32_t __mask = ao_arch_irqsave(); \
diff --git a/src/stm/ao_dma_stm.c b/src/stm/ao_dma_stm.c
index 6d779660..962b3acc 100644
--- a/src/stm/ao_dma_stm.c
+++ b/src/stm/ao_dma_stm.c
@@ -29,7 +29,6 @@ uint8_t ao_dma_done[NUM_DMA];
static struct ao_dma_config ao_dma_config[NUM_DMA];
static uint8_t ao_dma_allocated[NUM_DMA];
static uint8_t ao_dma_mutex[NUM_DMA];
-static uint8_t ao_dma_active;
static void
ao_dma_isr(uint8_t index) {
@@ -49,12 +48,24 @@ ao_dma_isr(uint8_t index) {
void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); }
+#ifdef STM_DMA1_3_STOLEN
+#define LEAVE_DMA_ON
+#else
void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); }
+#endif
void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); }
+#ifdef STM_DMA1_5_STOLEN
+#define LEAVE_DMA_ON
+#else
void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); }
+#endif
void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); }
void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
+#ifndef LEAVE_DMA_ON
+static uint8_t ao_dma_active;
+#endif
+
void
ao_dma_set_transfer(uint8_t index,
volatile void *peripheral,
@@ -68,10 +79,12 @@ ao_dma_set_transfer(uint8_t index,
ao_dma_mutex[index] = 0xff;
} else
ao_mutex_get(&ao_dma_mutex[index]);
+#ifndef LEAVE_DMA_ON
ao_arch_critical(
if (ao_dma_active++ == 0)
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
);
+#endif
stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
stm_dma.channel[index].cndtr = count;
stm_dma.channel[index].cpar = peripheral;
@@ -96,10 +109,12 @@ void
ao_dma_done_transfer(uint8_t index)
{
stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+#ifndef LEAVE_DMA_ON
ao_arch_critical(
if (--ao_dma_active == 0)
stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
);
+#endif
if (ao_dma_allocated[index])
ao_dma_mutex[index] = 0;
else
@@ -120,10 +135,12 @@ ao_dma_dump_cmd(void)
{
int i;
+#ifndef LEAVE_DMA_ON
ao_arch_critical(
if (ao_dma_active++ == 0)
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
);
+#endif
printf ("isr %08x ifcr%08x\n", stm_dma.isr, stm_dma.ifcr);
for (i = 0; i < NUM_DMA; i++)
printf("%d: done %d allocated %d mutex %2d ccr %04x cndtr %04x cpar %08x cmar %08x isr %08x\n",
@@ -136,10 +153,12 @@ ao_dma_dump_cmd(void)
stm_dma.channel[i].cpar,
stm_dma.channel[i].cmar,
ao_dma_config[i].isr);
+#ifndef LEAVE_DMA_ON
ao_arch_critical(
if (--ao_dma_active == 0)
stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
);
+#endif
}
static const struct ao_cmds ao_dma_cmds[] = {
@@ -153,9 +172,27 @@ ao_dma_init(void)
{
int index;
+#ifdef LEAVE_DMA_ON
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+#endif
for (index = 0; index < STM_NUM_DMA; index++) {
+#if STM_DMA1_5_STOLEN
+ if (index == STM_DMA_INDEX(5)) {
+ ao_dma_allocated[index] = 1;
+ ao_dma_mutex[index] = 0xff;
+ continue;
+ }
+#endif
+#if STM_DMA1_3_STOLEN
+ if (index == STM_DMA_INDEX(3)) {
+ ao_dma_allocated[index] = 1;
+ ao_dma_mutex[index] = 0xff;
+ continue;
+ }
+#endif
stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index);
- stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index, 4);
+ stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index,
+ AO_STM_NVIC_MED_PRIORITY);
ao_dma_allocated[index] = 0;
ao_dma_mutex[index] = 0;
}
diff --git a/src/stm/ao_exti_stm.c b/src/stm/ao_exti_stm.c
index 3e0b3e5c..2491b609 100644
--- a/src/stm/ao_exti_stm.c
+++ b/src/stm/ao_exti_stm.c
@@ -123,7 +123,7 @@ ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode) {
(void) gpio;
uint32_t mask = 1 << pin;
-
+
if (mode & AO_EXTI_MODE_RISING)
stm_exti.rtsr |= mask;
else
diff --git a/src/stm/ao_flash_stm.c b/src/stm/ao_flash_stm.c
index c1648421..38618bbe 100644
--- a/src/stm/ao_flash_stm.c
+++ b/src/stm/ao_flash_stm.c
@@ -74,11 +74,10 @@ static void __attribute__ ((section(".ramtext"),noinline))
_ao_flash_erase_page(uint32_t *page)
{
stm_flash.pecr |= (1 << STM_FLASH_PECR_ERASE) | (1 << STM_FLASH_PECR_PROG);
-
+
*page = 0x00000000;
- while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
- ;
+ ao_flash_wait_bsy();
}
void
@@ -101,9 +100,8 @@ _ao_flash_half_page(uint32_t *dst, uint32_t *src)
stm_flash.pecr |= (1 << STM_FLASH_PECR_FPRG);
stm_flash.pecr |= (1 << STM_FLASH_PECR_PROG);
-
- while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
- ;
+
+ ao_flash_wait_bsy();
for (i = 0; i < 32; i++) {
*dst++ = *src++;
diff --git a/src/stm/ao_i2c_stm.c b/src/stm/ao_i2c_stm.c
index 29a8f173..59cad495 100644
--- a/src/stm/ao_i2c_stm.c
+++ b/src/stm/ao_i2c_stm.c
@@ -75,6 +75,9 @@ uint8_t ao_i2c_mutex[STM_NUM_I2C];
#if AO_PCLK1 == 16000000
# define AO_STM_I2C_CR2_FREQ STM_I2C_CR2_FREQ_16_MHZ
#endif
+#if AO_PCLK1 == 24000000
+# define AO_STM_I2C_CR2_FREQ STM_I2C_CR2_FREQ_24_MHZ
+#endif
#if AO_PCLK1 == 32000000
# define AO_STM_I2C_CR2_FREQ STM_I2C_CR2_FREQ_32_MHZ
#endif
diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c
index db0be992..c625471e 100644
--- a/src/stm/ao_serial_stm.c
+++ b/src/stm/ao_serial_stm.c
@@ -444,7 +444,7 @@ ao_serial_init(void)
ao_usart_init(&ao_stm_usart1);
stm_nvic_set_enable(STM_ISR_USART1_POS);
- stm_nvic_set_priority(STM_ISR_USART1_POS, 4);
+ stm_nvic_set_priority(STM_ISR_USART1_POS, AO_STM_NVIC_MED_PRIORITY);
#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
ao_add_stdio(_ao_serial1_pollchar,
ao_serial1_putchar,
@@ -500,7 +500,7 @@ ao_serial_init(void)
#endif
stm_nvic_set_enable(STM_ISR_USART2_POS);
- stm_nvic_set_priority(STM_ISR_USART2_POS, 4);
+ stm_nvic_set_priority(STM_ISR_USART2_POS, AO_STM_NVIC_MED_PRIORITY);
#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
ao_add_stdio(_ao_serial2_pollchar,
ao_serial2_putchar,
@@ -544,7 +544,7 @@ ao_serial_init(void)
ao_usart_init(&ao_stm_usart3);
stm_nvic_set_enable(STM_ISR_USART3_POS);
- stm_nvic_set_priority(STM_ISR_USART3_POS, 4);
+ stm_nvic_set_priority(STM_ISR_USART3_POS, AO_STM_NVIC_MED_PRIORITY);
#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
ao_add_stdio(_ao_serial3_pollchar,
ao_serial3_putchar,
diff --git a/src/stm/ao_timer.c b/src/stm/ao_timer.c
index f86a5116..1576a6c9 100644
--- a/src/stm/ao_timer.c
+++ b/src/stm/ao_timer.c
@@ -90,6 +90,7 @@ ao_timer_init(void)
stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) |
(1 << STM_SYSTICK_CSR_TICKINT) |
(STM_SYSTICK_CSR_CLKSOURCE_HCLK_8 << STM_SYSTICK_CSR_CLKSOURCE));
+ stm_nvic.shpr15_12 |= AO_STM_NVIC_CLOCK_PRIORITY << 24;
}
#endif
diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c
index 9d72844e..33e0617c 100644
--- a/src/stm/ao_usb_stm.c
+++ b/src/stm/ao_usb_stm.c
@@ -1015,7 +1015,7 @@ ao_usb_enable(void)
ao_arch_block_interrupts();
/* Route interrupts */
- stm_nvic_set_priority(STM_ISR_USB_LP_POS, 3);
+ stm_nvic_set_priority(STM_ISR_USB_LP_POS, AO_STM_NVIC_LOW_PRIORITY);
stm_nvic_set_enable(STM_ISR_USB_LP_POS);
ao_usb_configuration = 0;
@@ -1109,7 +1109,7 @@ struct ao_usb_dbg {
int line;
char *msg;
uint32_t value;
- uint32_t primask;
+ uint32_t prival;
#if TX_DBG
uint16_t in_count;
uint32_t in_epr;
@@ -1125,19 +1125,23 @@ struct ao_usb_dbg {
#endif
};
-#define NUM_USB_DBG 128
+#define NUM_USB_DBG 16
-static struct ao_usb_dbg dbg[128];
+static struct ao_usb_dbg dbg[NUM_USB_DBG];
static int dbg_i;
static void _dbg(int line, char *msg, uint32_t value)
{
- uint32_t primask;
+ uint32_t prival;
dbg[dbg_i].line = line;
dbg[dbg_i].msg = msg;
dbg[dbg_i].value = value;
- asm("mrs %0,primask" : "=&r" (primask));
- dbg[dbg_i].primask = primask;
+#if AO_NONMASK_INTERRUPT
+ asm("mrs %0,basepri" : "=&r" (prival));
+#else
+ asm("mrs %0,primask" : "=&r" (prival));
+#endif
+ dbg[dbg_i].prival = prival;
#if TX_DBG
dbg[dbg_i].in_count = in_count;
dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
diff --git a/src/stm/stm32l.h b/src/stm/stm32l.h
index be1e1d65..201f4f36 100644
--- a/src/stm/stm32l.h
+++ b/src/stm/stm32l.h
@@ -52,7 +52,32 @@ stm_moder_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
~(STM_MODER_MASK << STM_MODER_SHIFT(pin))) |
value << STM_MODER_SHIFT(pin));
}
-
+
+static inline uint32_t
+stm_spread_mask(uint16_t mask) {
+ uint32_t m = mask;
+
+ /* 0000000000000000mmmmmmmmmmmmmmmm */
+ m = (m & 0xff) | ((m & 0xff00) << 8);
+ /* 00000000mmmmmmmm00000000mmmmmmmm */
+ m = (m & 0x000f000f) | ((m & 0x00f000f0) << 4);
+ /* 0000mmmm0000mmmm0000mmmm0000mmmm */
+ m = (m & 0x03030303) | ((m & 0x0c0c0c0c) << 2);
+ /* 00mm00mm00mm00mm00mm00mm00mm00mm */
+ m = (m & 0x11111111) | ((m & 0x22222222) << 2);
+ /* 0m0m0m0m0m0m0m0m0m0m0m0m0m0m0m0m */
+ return m;
+}
+
+static inline void
+stm_moder_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+ uint32_t bits32 = stm_spread_mask(mask);
+ uint32_t mask32 = 3 * bits32;
+ uint32_t value32 = (value & 3) * bits32;
+
+ gpio->moder = ((gpio->moder & ~mask32) | value32);
+}
+
static inline uint32_t
stm_moder_get(struct stm_gpio *gpio, int pin) {
return (gpio->moder >> STM_MODER_SHIFT(pin)) & STM_MODER_MASK;
@@ -69,7 +94,7 @@ stm_otyper_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
~(STM_OTYPER_MASK << STM_OTYPER_SHIFT(pin))) |
value << STM_OTYPER_SHIFT(pin));
}
-
+
static inline uint32_t
stm_otyper_get(struct stm_gpio *gpio, int pin) {
return (gpio->otyper >> STM_OTYPER_SHIFT(pin)) & STM_OTYPER_MASK;
@@ -83,12 +108,21 @@ stm_otyper_get(struct stm_gpio *gpio, int pin) {
#define STM_OSPEEDR_40MHz 3
static inline void
-stm_ospeedr_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+stm_ospeedr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
gpio->ospeedr = ((gpio->ospeedr &
~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) |
value << STM_OSPEEDR_SHIFT(pin));
}
-
+
+static inline void
+stm_ospeedr_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+ uint32_t bits32 = stm_spread_mask(mask);
+ uint32_t mask32 = 3 * bits32;
+ uint32_t value32 = (value & 3) * bits32;
+
+ gpio->ospeedr = ((gpio->ospeedr & ~mask32) | value32);
+}
+
static inline uint32_t
stm_ospeedr_get(struct stm_gpio *gpio, int pin) {
return (gpio->ospeedr >> STM_OSPEEDR_SHIFT(pin)) & STM_OSPEEDR_MASK;
@@ -107,7 +141,16 @@ stm_pupdr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
~(STM_PUPDR_MASK << STM_PUPDR_SHIFT(pin))) |
value << STM_PUPDR_SHIFT(pin));
}
-
+
+static inline void
+stm_pupdr_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+ uint32_t bits32 = stm_spread_mask(mask);
+ uint32_t mask32 = 3 * bits32;
+ uint32_t value32 = (value & 3) * bits32;
+
+ gpio->pupdr = (gpio->pupdr & ~mask32) | value32;
+}
+
static inline uint32_t
stm_pupdr_get(struct stm_gpio *gpio, int pin) {
return (gpio->pupdr >> STM_PUPDR_SHIFT(pin)) & STM_PUPDR_MASK;
@@ -168,6 +211,12 @@ stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
}
static inline void
+stm_gpio_set_mask(struct stm_gpio *gpio, uint16_t bits, uint16_t mask) {
+ /* Use the bit set/reset register to do this atomically */
+ gpio->bsrr = ((uint32_t) (~bits & mask) << 16) | ((uint32_t) (bits & mask));
+}
+
+static inline void
stm_gpio_set_bits(struct stm_gpio *gpio, uint16_t bits) {
gpio->bsrr = bits;
}
@@ -518,7 +567,7 @@ extern struct stm_rcc stm_rcc;
#define STM_RCC_CFGR_MCOPRE_DIV_4 2
#define STM_RCC_CFGR_MCOPRE_DIV_8 3
#define STM_RCC_CFGR_MCOPRE_DIV_16 4
-#define STM_RCC_CFGR_MCOPRE_DIV_MASK 7
+#define STM_RCC_CFGR_MCOPRE_MASK 7
#define STM_RCC_CFGR_MCOSEL (24)
#define STM_RCC_CFGR_MCOSEL_DISABLE 0
@@ -897,7 +946,11 @@ struct stm_nvic {
vuint32_t sc; /* 0xc10 0xe000ed10 System Control Register */
vuint32_t cc; /* 0xc14 0xe000ed14 Configuration Control Register */
- uint8_t _unusedc18[0xe00 - 0xc18];
+ vuint32_t shpr7_4; /* 0xc18 0xe000ed18 System Hander Priority Registers */
+ vuint32_t shpr11_8; /* 0xc1c */
+ vuint32_t shpr15_12; /* 0xc20 */
+
+ uint8_t _unusedc18[0xe00 - 0xc24];
vuint32_t stir; /* 0xe00 */
};
@@ -1594,6 +1647,7 @@ extern struct stm_i2c stm_i2c1, stm_i2c2;
#define STM_I2C_CR2_FREQ_4_MHZ 4
#define STM_I2C_CR2_FREQ_8_MHZ 8
#define STM_I2C_CR2_FREQ_16_MHZ 16
+#define STM_I2C_CR2_FREQ_24_MHZ 24
#define STM_I2C_CR2_FREQ_32_MHZ 32
#define STM_I2C_CR2_FREQ_MASK 0x3f
@@ -1740,6 +1794,12 @@ extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
#define STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK 7
#define STM_TIM234_SMCR_SMS_MASK 7
+#define STM_TIM234_DIER_CC4IE 4
+#define STM_TIM234_DIER_CC3IE 3
+#define STM_TIM234_DIER_CC2IE 2
+#define STM_TIM234_DIER_CC1IE 1
+#define STM_TIM234_DIER_UIE 0
+
#define STM_TIM234_SR_CC4OF 12
#define STM_TIM234_SR_CC3OF 11
#define STM_TIM234_SR_CC2OF 10
@@ -1840,15 +1900,23 @@ extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
#define STM_TIM234_CCER_CC4NP 15
#define STM_TIM234_CCER_CC4P 13
+#define STM_TIM234_CCER_CC4P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC4P_ACTIVE_LOW 1
#define STM_TIM234_CCER_CC4E 12
#define STM_TIM234_CCER_CC3NP 11
#define STM_TIM234_CCER_CC3P 9
+#define STM_TIM234_CCER_CC3P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC3P_ACTIVE_LOW 1
#define STM_TIM234_CCER_CC3E 8
#define STM_TIM234_CCER_CC2NP 7
#define STM_TIM234_CCER_CC2P 5
+#define STM_TIM234_CCER_CC2P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC2P_ACTIVE_LOW 1
#define STM_TIM234_CCER_CC2E 4
#define STM_TIM234_CCER_CC1NP 3
#define STM_TIM234_CCER_CC1P 1
+#define STM_TIM234_CCER_CC1P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC1P_ACTIVE_LOW 1
#define STM_TIM234_CCER_CC1E 0
struct stm_usb {
diff --git a/src/stmf0/Makefile-stmf0.defs b/src/stmf0/Makefile-stmf0.defs
index f3296b69..f2c53499 100644
--- a/src/stmf0/Makefile-stmf0.defs
+++ b/src/stmf0/Makefile-stmf0.defs
@@ -4,7 +4,7 @@ endif
include $(TOPDIR)/Makedefs
-vpath % $(TOPDIR)/stmf0:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR/aes):$(TOPDIR):$(TOPDIR)/math
+vpath % $(TOPDIR)/stmf0:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR)/aes:$(TOPDIR):$(TOPDIR)/math:$(TOPDIR)/lisp
vpath make-altitude $(TOPDIR)/util
vpath make-kalman $(TOPDIR)/util
vpath kalman.5c $(TOPDIR)/kalman
@@ -27,7 +27,10 @@ CC=$(ARM_CC)
WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align
-AO_CFLAGS=-I. -I$(TOPDIR)/stmf0 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math $(PDCLIB_INCLUDES)
+AO_CFLAGS=-I. -I$(TOPDIR)/stmf0 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \
+ -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math -I$(TOPDIR)/lisp \
+ $(PDCLIB_INCLUDES)
+
STMF0_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m0 -mthumb\
-ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
diff --git a/src/stmf0/altos-raw.ld b/src/stmf0/altos-raw.ld
new file mode 100644
index 00000000..eb285e07
--- /dev/null
+++ b/src/stmf0/altos-raw.ld
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 6k - 128
+ stack (!w) : ORIGIN = 0x20000000 + 6k - 128, LENGTH = 128
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .interrupt : {
+ __text_start__ = .;
+ *(.interrupt) /* Interrupt vectors */
+ } > rom
+
+ .text ORIGIN(rom) + 0x100 : {
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+
+ *(.text*) /* Executable code */
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ *(.rodata*) /* Constants */
+
+ } > rom
+ __text_end__ = .;
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ __data_start__ = .;
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+}
+
+ENTRY(start);
+
+
diff --git a/src/stmf0/altos.ld b/src/stmf0/altos.ld
index 8f8933c6..74fdf3ea 100644
--- a/src/stmf0/altos.ld
+++ b/src/stmf0/altos.ld
@@ -55,10 +55,16 @@ SECTIONS {
ao_product.o(.romconfig*)
*(.text*) /* Executable code */
+ } > rom
+
+ .ARM.exidx : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
- *(.rodata*) /* Constants */
+ } > rom
+ .rodata : {
+ *(.rodata*) /* Constants */
} > rom
+
__text_end__ = .;
/* Boot data which must live at the start of ram so that
diff --git a/src/stmf0/ao_adc_stm.c b/src/stmf0/ao_adc_stm.c
new file mode 100644
index 00000000..2b23dc50
--- /dev/null
+++ b/src/stmf0/ao_adc_stm.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_data.h>
+
+#define AO_ADC_DEBUG 0
+
+static uint8_t ao_adc_ready;
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(int index)
+{
+ (void) index;
+ /* Clear ISR bits */
+ stm_adc.isr = ((1 << STM_ADC_ISR_AWD) |
+ (1 << STM_ADC_ISR_OVR) |
+ (1 << STM_ADC_ISR_EOSEQ) |
+ (1 << STM_ADC_ISR_EOC));
+
+ AO_DATA_PRESENT(AO_DATA_ADC);
+ ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+ if (ao_data_present == AO_DATA_ALL) {
+#if HAS_MS5607
+ ao_data_ring[ao_data_head].ms5607_raw = ao_ms5607_current;
+#endif
+#if HAS_MMA655X
+ ao_data_ring[ao_data_head].mma655x = ao_mma655x_current;
+#endif
+#if HAS_HMC5883
+ ao_data_ring[ao_data_head].hmc5883 = ao_hmc5883_current;
+#endif
+#if HAS_MPU6000
+ ao_data_ring[ao_data_head].mpu6000 = ao_mpu6000_current;
+#endif
+ ao_data_ring[ao_data_head].tick = ao_tick_count;
+ ao_data_head = ao_data_ring_next(ao_data_head);
+ ao_wakeup((void *) &ao_data_head);
+ }
+ ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+ if (!ao_adc_ready)
+ return;
+ ao_adc_ready = 0;
+ stm_adc.isr = 0;
+ ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1),
+ &stm_adc.dr,
+ (void *) (&ao_data_ring[ao_data_head].adc),
+ AO_NUM_ADC,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR) |
+ (1 << STM_DMA_CCR_TCIE));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_done);
+ ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+
+ stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
+}
+
+static void
+ao_adc_dump(void)
+{
+ struct ao_data packet;
+
+ ao_data_get(&packet);
+ AO_ADC_DUMP(&packet);
+}
+
+#if AO_ADC_DEBUG
+static void
+ao_adc_one(void)
+{
+ int ch;
+ uint16_t value;
+
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ ch = ao_cmd_lex_i;
+ if (ch < 0 || AO_NUM_ADC <= ch) {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
+
+ ao_timer_set_adc_interval(0);
+ ao_delay(1);
+
+ printf("At top, data %u isr %04x cr %04x\n", stm_adc.dr, stm_adc.isr, stm_adc.cr);
+
+ if (stm_adc.cr & (1 << STM_ADC_CR_ADEN)) {
+ printf("Disabling\n"); flush();
+ stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
+ while (stm_adc.cr & (1 << STM_ADC_CR_ADDIS))
+ ;
+ printf("Disabled\n"); flush();
+ }
+
+ /* Turn off everything */
+ stm_adc.cr &= ~((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADSTP) |
+ (1 << STM_ADC_CR_ADSTART) |
+ (1 << STM_ADC_CR_ADEN));
+
+ printf("After disable, ADC status %04x\n", stm_adc.cr);
+
+ /* Configure */
+ stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) | /* analog watchdog channel 0 */
+ (0 << STM_ADC_CFGR1_AWDEN) | /* Disable analog watchdog */
+ (0 << STM_ADC_CFGR1_AWDSGL) | /* analog watchdog on all channels */
+ (0 << STM_ADC_CFGR1_DISCEN) | /* Not discontinuous mode. All channels converted with one trigger */
+ (0 << STM_ADC_CFGR1_AUTOOFF) | /* Leave ADC running */
+ (1 << STM_ADC_CFGR1_WAIT) | /* Wait for data to be read before next conversion */
+ (0 << STM_ADC_CFGR1_CONT) | /* only one set of conversions per trigger */
+ (1 << STM_ADC_CFGR1_OVRMOD) | /* overwrite on overrun */
+ (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) | /* SW trigger */
+ (0 << STM_ADC_CFGR1_ALIGN) | /* Align to LSB */
+ (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) | /* 12 bit resolution */
+ (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) | /* scan 0 .. n */
+ (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
+ (0 << STM_ADC_CFGR1_DMAEN)); /* disable DMA */
+
+ stm_adc.chselr = (1 << ch);
+
+ /* Longest sample time */
+ stm_adc.smpr = STM_ADC_SMPR_SMP_41_5 << STM_ADC_SMPR_SMP;
+
+ printf("Before enable, ADC status %04x\n", stm_adc.cr); flush();
+ /* Enable */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
+ while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
+ ;
+
+ /* Start */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
+
+ /* Wait for conversion complete */
+ while (!(stm_adc.isr & (1 << STM_ADC_ISR_EOC)))
+ ;
+
+ value = stm_adc.dr;
+ printf ("value %u, cr is %04x isr is %04x\n",
+ value, stm_adc.cr, stm_adc.isr);
+
+
+ /* Clear ISR bits */
+ stm_adc.isr = ((1 << STM_ADC_ISR_AWD) |
+ (1 << STM_ADC_ISR_OVR) |
+ (1 << STM_ADC_ISR_EOSEQ) |
+ (1 << STM_ADC_ISR_EOC));
+}
+#endif
+
+__code struct ao_cmds ao_adc_cmds[] = {
+ { ao_adc_dump, "a\0Display current ADC values" },
+#if AO_ADC_DEBUG
+ { ao_adc_one, "A ch\0Display one ADC channel" },
+#endif
+ { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+ uint32_t chselr;
+
+ /* Reset ADC */
+ stm_rcc.apb2rstr |= (1 << STM_RCC_APB2RSTR_ADCRST);
+ stm_rcc.apb2rstr &= ~(1 << STM_RCC_APB2RSTR_ADCRST);
+
+ /* Turn on ADC pins */
+ stm_rcc.ahbenr |= AO_ADC_RCC_AHBENR;
+
+#ifdef AO_ADC_PIN0_PORT
+ stm_moder_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+ stm_moder_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+ stm_moder_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+ stm_moder_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+ stm_moder_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+ stm_moder_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+ stm_moder_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+ stm_moder_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+ #error "Too many ADC ports"
+#endif
+
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADCEN);
+
+ chselr = 0;
+#if AO_NUM_ADC > 0
+ chselr |= (1 << AO_ADC_PIN0_CH);
+#endif
+#if AO_NUM_ADC > 1
+ chselr |= (1 << AO_ADC_PIN1_CH);
+#endif
+#if AO_NUM_ADC > 2
+ chselr |= (1 << AO_ADC_PIN2_CH);
+#endif
+#if AO_NUM_ADC > 3
+ chselr |= (1 << AO_ADC_PIN3_CH);
+#endif
+#if AO_NUM_ADC > 4
+ chselr |= (1 << AO_ADC_PIN4_CH);
+#endif
+#if AO_NUM_ADC > 5
+ chselr |= (1 << AO_ADC_PIN5_CH);
+#endif
+#if AO_NUM_ADC > 6
+ chselr |= (1 << AO_ADC_PIN6_CH);
+#endif
+#if AO_NUM_ADC > 7
+ chselr |= (1 << AO_ADC_PIN7_CH);
+#endif
+#if AO_NUM_ADC > 8
+#error Need more ADC defines
+#endif
+
+ /* Wait for ADC to be idle */
+ while (stm_adc.cr & ((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADDIS)))
+ ;
+
+ /* Disable */
+ if (stm_adc.cr & (1 << STM_ADC_CR_ADEN)) {
+ stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
+ while (stm_adc.cr & (1 << STM_ADC_CR_ADDIS))
+ ;
+ }
+
+ /* Turn off everything */
+ stm_adc.cr &= ~((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADSTP) |
+ (1 << STM_ADC_CR_ADSTART) |
+ (1 << STM_ADC_CR_ADEN));
+
+ /* Configure */
+ stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) | /* analog watchdog channel 0 */
+ (0 << STM_ADC_CFGR1_AWDEN) | /* Disable analog watchdog */
+ (0 << STM_ADC_CFGR1_AWDSGL) | /* analog watchdog on all channels */
+ (0 << STM_ADC_CFGR1_DISCEN) | /* Not discontinuous mode. All channels converted with one trigger */
+ (0 << STM_ADC_CFGR1_AUTOOFF) | /* Leave ADC running */
+ (1 << STM_ADC_CFGR1_WAIT) | /* Wait for data to be read before next conversion */
+ (0 << STM_ADC_CFGR1_CONT) | /* only one set of conversions per trigger */
+ (1 << STM_ADC_CFGR1_OVRMOD) | /* overwrite on overrun */
+ (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) | /* SW trigger */
+ (0 << STM_ADC_CFGR1_ALIGN) | /* Align to LSB */
+ (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) | /* 12 bit resolution */
+ (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) | /* scan 0 .. n */
+ (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
+ (1 << STM_ADC_CFGR1_DMAEN)); /* enable DMA */
+
+ /* Set the clock */
+ stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
+
+ /* Shortest sample time */
+ stm_adc.smpr = STM_ADC_SMPR_SMP_71_5 << STM_ADC_SMPR_SMP;
+
+ stm_adc.chselr = chselr;
+
+ stm_adc.ccr = ((0 << STM_ADC_CCR_VBATEN) |
+ (0 << STM_ADC_CCR_TSEN) |
+ (0 << STM_ADC_CCR_VREFEN));
+
+ /* Calibrate */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADCAL);
+ while ((stm_adc.cr & (1 << STM_ADC_CR_ADCAL)) != 0)
+ ;
+
+ /* Enable */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
+ while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
+ ;
+
+ /* Clear any stale status bits */
+ stm_adc.isr = 0;
+
+ /* Turn on syscfg */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
+
+ /* Set ADC to use DMA channel 1 (option 1) */
+ stm_syscfg.cfgr1 &= ~(1 << STM_SYSCFG_CFGR1_ADC_DMA_RMP);
+
+ ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+
+ ao_cmd_register(&ao_adc_cmds[0]);
+
+ ao_adc_ready = 1;
+}
diff --git a/src/stmf0/ao_arch.h b/src/stmf0/ao_arch.h
index a36482b6..c5f451f5 100644
--- a/src/stmf0/ao_arch.h
+++ b/src/stmf0/ao_arch.h
@@ -144,10 +144,15 @@ ao_adc_init();
/* ADC maximum reported value */
#define AO_ADC_MAX 4095
+#ifndef HAS_BOOT_LOADER
+#define HAS_BOOT_LOADER 1
+#endif
+
+#if HAS_BOOT_LOADER
#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x08001000)
#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x08000000 + stm_flash_size()))
#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x08000000)
-#define HAS_BOOT_LOADER 1
+#endif
#endif /* _AO_ARCH_H_ */
diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h
index 0cb0e43d..c38ce41a 100644
--- a/src/stmf0/ao_arch_funcs.h
+++ b/src/stmf0/ao_arch_funcs.h
@@ -314,7 +314,18 @@ struct ao_stm_usart {
struct ao_fifo rx_fifo;
struct ao_fifo tx_fifo;
struct stm_usart *reg;
- uint8_t tx_started;
+ uint8_t tx_running;
+ uint8_t draining;
+#if HAS_SERIAL_SW_FLOW
+ /* RTS - 0 if we have FIFO space, 1 if not
+ * CTS - 0 if we can send, 0 if not
+ */
+ struct stm_gpio *gpio_rts;
+ struct stm_gpio *gpio_cts;
+ uint8_t pin_rts;
+ uint8_t pin_cts;
+ uint8_t rts;
+#endif
};
#if HAS_SERIAL_1
diff --git a/src/stmf0/ao_beep_stm.c b/src/stmf0/ao_beep_stm.c
new file mode 100644
index 00000000..610f4a31
--- /dev/null
+++ b/src/stmf0/ao_beep_stm.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#ifndef BEEPER_CHANNEL
+#error BEEPER_CHANNEL undefined
+#endif
+
+#ifndef BEEPER_TIMER
+#define BEEPER_TIMER 1
+#endif
+
+#if BEEPER_TIMER == 1
+#define timer stm_tim1
+#define STM_RCC_TIMER STM_RCC_APB2ENR_TIM1EN
+#define stm_rcc_enr stm_rcc.apb2enr
+#endif
+
+#if BEEPER_TIMER == 2
+#define timer stm_tim2
+#define STM_RCC_TIMER STM_RCC_APB1ENR_TIM2EN
+#define stm_rcc_enr stm_rcc.apb1enr
+#endif
+
+#if BEEPER_TIMER == 3
+#define timer stm_tim3
+#define STM_RCC_TIMER STM_RCC_APB1ENR_TIM3EN
+#define stm_rcc_enr stm_rcc.apb1enr
+#endif
+
+#ifndef timer
+#error BEEPER_TIMER invalid
+#endif
+
+static inline void
+disable(void)
+{
+ timer.cr1 = 0;
+#if BEEPER_TIMER == 1
+ timer.bdtr = 0;
+#endif
+ stm_rcc_enr &= ~(1 << STM_RCC_TIMER);
+
+ /* Disconnect the timer from the pin */
+ stm_afr_set(BEEPER_PORT, BEEPER_PIN, STM_AFR_NONE);
+}
+
+void
+ao_beep(uint8_t beep)
+{
+ if (beep == 0) {
+ disable();
+ } else {
+ stm_rcc_enr |= (1 << STM_RCC_TIMER);
+
+#if BEEPER_TIMER == 1
+ /* Master output enable */
+ stm_tim1.bdtr = (1 << STM_TIM1_BDTR_MOE);
+
+ stm_tim1.cr2 = ((0 << STM_TIM1_CR2_TI1S) |
+ (STM_TIM1_CR2_MMS_RESET << STM_TIM1_CR2_MMS) |
+ (0 << STM_TIM1_CR2_CCDS));
+
+ /* Set prescaler to match cc1111 clocks
+ */
+ stm_tim1.psc = AO_TIM_CLK / 750000;
+
+ /* 1. Select the counter clock (internal, external, prescaler).
+ *
+ * Setting SMCR to zero means use the internal clock
+ */
+
+ stm_tim1.smcr = 0;
+
+ /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+ stm_tim1.arr = beep;
+ stm_tim1.ccr1 = beep;
+
+ /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+ * DMA request is to be generated.
+ */
+ /* don't want this */
+
+ /* 4. Select the output mode. For example, you must write
+ * OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+ * pin when CNT matches CCRx, CCRx preload is not used, OCx
+ * is enabled and active high.
+ */
+
+#if BEEPER_CHANNEL == 1
+ stm_tim1.ccmr1 = ((0 << STM_TIM1_CCMR1_OC2CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR1_OC2M) |
+ (0 << STM_TIM1_CCMR1_OC2PE) |
+ (0 << STM_TIM1_CCMR1_OC2FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC2S) |
+
+ (0 << STM_TIM1_CCMR1_OC1CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR1_OC1M) |
+ (0 << STM_TIM1_CCMR1_OC1PE) |
+ (0 << STM_TIM1_CCMR1_OC1FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC1S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (1 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 2
+ stm_tim1.ccmr1 = ((0 << STM_TIM1_CCMR1_OC2CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR1_OC2M) |
+ (0 << STM_TIM1_CCMR1_OC2PE) |
+ (0 << STM_TIM1_CCMR1_OC2FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC2S) |
+
+ (0 << STM_TIM1_CCMR1_OC1CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR1_OC1M) |
+ (0 << STM_TIM1_CCMR1_OC1PE) |
+ (0 << STM_TIM1_CCMR1_OC1FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC1S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (1 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 3
+ stm_tim1.ccmr2 = ((0 << STM_TIM1_CCMR2_OC4CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR2_OC4M) |
+ (0 << STM_TIM1_CCMR2_OC4PE) |
+ (0 << STM_TIM1_CCMR2_OC4FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR2_CC4S) |
+
+ (0 << STM_TIM1_CCMR2_OC3CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR2_OC3M) |
+ (0 << STM_TIM1_CCMR2_OC3PE) |
+ (0 << STM_TIM1_CCMR2_OC3FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR2_CC3S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (1 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 4
+ stm_tim1.ccmr2 = ((0 << STM_TIM1_CCMR2_OC4CE) |
+ (STM_TIM1_CCMR2_OC4M_TOGGLE << STM_TIM1_CCMR2_OC4M) |
+ (0 << STM_TIM1_CCMR2_OC4PE) |
+ (0 << STM_TIM1_CCMR2_OC4FE) |
+ (STM_TIM1_CCMR2_CC4S_OUTPUT << STM_TIM1_CCMR2_CC4S) |
+
+ (0 << STM_TIM1_CCMR2_OC3CE) |
+ (STM_TIM1_CCMR2_OC3M_FROZEN << STM_TIM1_CCMR2_OC3M) |
+ (0 << STM_TIM1_CCMR2_OC3PE) |
+ (0 << STM_TIM1_CCMR2_OC3FE) |
+ (STM_TIM1_CCMR2_CC3S_OUTPUT << STM_TIM1_CCMR2_CC3S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4NP) |
+ (0 << STM_TIM1_CCER_CC4P) |
+ (1 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NP) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+ /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+ stm_tim1.cr1 = ((STM_TIM1_CR1_CKD_1 << STM_TIM1_CR1_CKD) |
+ (0 << STM_TIM1_CR1_ARPE) |
+ (STM_TIM1_CR1_CMS_EDGE << STM_TIM1_CR1_CMS) |
+ (0 << STM_TIM1_CR1_DIR) |
+ (0 << STM_TIM1_CR1_OPM) |
+ (0 << STM_TIM1_CR1_URS) |
+ (0 << STM_TIM1_CR1_UDIS) |
+ (1 << STM_TIM1_CR1_CEN));
+
+ /* Update the values */
+ stm_tim1.egr = (1 << STM_TIM1_EGR_UG);
+#endif
+#if BEEPER_TIMER == 2 || BEEPER_TIMER == 3
+
+ timer.cr2 = ((0 << STM_TIM23_CR2_TI1S) |
+ (STM_TIM23_CR2_MMS_RESET << STM_TIM23_CR2_MMS) |
+ (0 << STM_TIM23_CR2_CCDS));
+
+ /* Set prescaler to match cc1111 clocks
+ */
+ timer.psc = AO_TIM_CLK / 750000;
+
+ /* 1. Select the counter clock (internal, external, prescaler).
+ *
+ * Setting SMCR to zero means use the internal clock
+ */
+
+ timer.smcr = 0;
+
+ /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+ timer.arr = beep;
+ timer.ccr1 = beep;
+
+ /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+ * DMA request is to be generated.
+ */
+ /* don't want this */
+
+ /* 4. Select the output mode. For example, you must write
+ * OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+ * pin when CNT matches CCRx, CCRx preload is not used, OCx
+ * is enabled and active high.
+ */
+
+#if BEEPER_CHANNEL == 1
+ timer.ccmr1 = ((0 << STM_TIM23_CCMR1_OC2CE) |
+ (STM_TIM23_CCMR1_OC2M_FROZEN << STM_TIM23_CCMR1_OC2M) |
+ (0 << STM_TIM23_CCMR1_OC2PE) |
+ (0 << STM_TIM23_CCMR1_OC2FE) |
+ (STM_TIM23_CCMR1_CC2S_OUTPUT << STM_TIM23_CCMR1_CC2S) |
+
+ (0 << STM_TIM23_CCMR1_OC1CE) |
+ (STM_TIM23_CCMR1_OC1M_TOGGLE << STM_TIM23_CCMR1_OC1M) |
+ (0 << STM_TIM23_CCMR1_OC1PE) |
+ (0 << STM_TIM23_CCMR1_OC1FE) |
+ (STM_TIM23_CCMR1_CC1S_OUTPUT << STM_TIM23_CCMR1_CC1S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (1 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 2
+ timer.ccmr1 = ((0 << STM_TIM23_CCMR1_OC2CE) |
+ (STM_TIM23_CCMR1_OC2M_TOGGLE << STM_TIM23_CCMR1_OC2M) |
+ (0 << STM_TIM23_CCMR1_OC2PE) |
+ (0 << STM_TIM23_CCMR1_OC2FE) |
+ (STM_TIM23_CCMR1_CC2S_OUTPUT << STM_TIM23_CCMR1_CC2S) |
+
+ (0 << STM_TIM23_CCMR1_OC1CE) |
+ (STM_TIM23_CCMR1_OC1M_FROZEN << STM_TIM23_CCMR1_OC1M) |
+ (0 << STM_TIM23_CCMR1_OC1PE) |
+ (0 << STM_TIM23_CCMR1_OC1FE) |
+ (STM_TIM23_CCMR1_CC1S_OUTPUT << STM_TIM23_CCMR1_CC1S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (1 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 3
+ timer.ccmr2 = ((0 << STM_TIM23_CCMR2_OC4CE) |
+ (STM_TIM23_CCMR2_OC4M_FROZEN << STM_TIM23_CCMR2_OC4M) |
+ (0 << STM_TIM23_CCMR2_OC4PE) |
+ (0 << STM_TIM23_CCMR2_OC4FE) |
+ (STM_TIM23_CCMR2_CC4S_OUTPUT << STM_TIM23_CCMR2_CC4S) |
+
+ (0 << STM_TIM23_CCMR2_OC3CE) |
+ (STM_TIM23_CCMR2_OC3M_TOGGLE << STM_TIM23_CCMR2_OC3M) |
+ (0 << STM_TIM23_CCMR2_OC3PE) |
+ (0 << STM_TIM23_CCMR2_OC3FE) |
+ (STM_TIM23_CCMR2_CC3S_OUTPUT << STM_TIM23_CCMR2_CC3S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (1 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 4
+ timer.ccmr2 = ((0 << STM_TIM23_CCMR2_OC4CE) |
+ (STM_TIM23_CCMR2_OC4M_TOGGLE << STM_TIM23_CCMR2_OC4M) |
+ (0 << STM_TIM23_CCMR2_OC4PE) |
+ (0 << STM_TIM23_CCMR2_OC4FE) |
+ (STM_TIM23_CCMR2_CC4S_OUTPUT << STM_TIM23_CCMR2_CC4S) |
+
+ (0 << STM_TIM23_CCMR2_OC3CE) |
+ (STM_TIM23_CCMR2_OC3M_FROZEN << STM_TIM23_CCMR2_OC3M) |
+ (0 << STM_TIM23_CCMR2_OC3PE) |
+ (0 << STM_TIM23_CCMR2_OC3FE) |
+ (STM_TIM23_CCMR2_CC3S_OUTPUT << STM_TIM23_CCMR2_CC3S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (1 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+ /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+ timer.cr1 = ((STM_TIM23_CR1_CKD_1 << STM_TIM23_CR1_CKD) |
+ (0 << STM_TIM23_CR1_ARPE) |
+ (STM_TIM23_CR1_CMS_EDGE << STM_TIM23_CR1_CMS) |
+ (0 << STM_TIM23_CR1_DIR) |
+ (0 << STM_TIM23_CR1_OPM) |
+ (0 << STM_TIM23_CR1_URS) |
+ (0 << STM_TIM23_CR1_UDIS) |
+ (1 << STM_TIM23_CR1_CEN));
+
+ /* Update the values */
+ timer.egr = (1 << STM_TIM23_EGR_UG);
+
+ /* Hook the timer up to the beeper pin */
+ stm_afr_set(BEEPER_PORT, BEEPER_PIN, STM_AFR_AF2);
+#endif
+ }
+}
+
+void
+ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant
+{
+ ao_beep(beep);
+ ao_delay(ticks);
+ ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+ ao_enable_output(BEEPER_PORT, BEEPER_PIN, BEEPER, 0);
+
+ /* Leave the timer off until requested */
+ stm_rcc_enr &= ~(1 << STM_RCC_TIMER);
+}
diff --git a/src/stmf0/ao_crc.h b/src/stmf0/ao_crc.h
index 7acc6f9c..b6d91023 100644
--- a/src/stmf0/ao_crc.h
+++ b/src/stmf0/ao_crc.h
@@ -35,7 +35,8 @@
static inline uint16_t
ao_crc_in_32_out_16(uint32_t v) {
stm_crc.dr.u32 = v;
- return stm_crc.dr.u16;
+ v = stm_crc.dr.u32;
+ return v ^ (v >> 16);
}
static inline uint16_t
diff --git a/src/stmf0/ao_flash_stm.c b/src/stmf0/ao_flash_stm.c
index 2aeff388..2d57eea7 100644
--- a/src/stmf0/ao_flash_stm.c
+++ b/src/stmf0/ao_flash_stm.c
@@ -19,6 +19,12 @@
#include <ao.h>
#include <ao_flash.h>
+/* Note that the HSI clock must be running for this code to work.
+ * Also, special care must be taken with the linker to ensure that the
+ * functions marked 'ramtext' land in ram and not rom. An example of that
+ * can be found in altos-loader.ld
+ */
+
static uint8_t
ao_flash_is_locked(void)
{
@@ -44,12 +50,7 @@ ao_flash_lock(void)
stm_flash.cr |= (1 << STM_FLASH_CR_LOCK);
}
-static void
-ao_flash_wait_bsy(void)
-{
- while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
- ;
-}
+#define ao_flash_wait_bsy() do { while (stm_flash.sr & (1 << STM_FLASH_SR_BSY)); } while (0)
static void __attribute__ ((section(".ramtext"),noinline))
_ao_flash_erase_page(uint32_t *page)
diff --git a/src/stmf0/ao_interrupt.c b/src/stmf0/ao_interrupt.c
index 79412483..fcd330f1 100644
--- a/src/stmf0/ao_interrupt.c
+++ b/src/stmf0/ao_interrupt.c
@@ -26,9 +26,11 @@
#define IS_FLASH_LOADER 0
#endif
+#ifndef RELOCATE_INTERRUPT
#if !IS_FLASH_LOADER
#define RELOCATE_INTERRUPT 1
#endif
+#endif
extern void main(void);
extern char __stack__;
diff --git a/src/stmf0/ao_serial_stm.c b/src/stmf0/ao_serial_stm.c
new file mode 100644
index 00000000..30b0dbd2
--- /dev/null
+++ b/src/stmf0/ao_serial_stm.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+void
+ao_debug_out(char c)
+{
+ if (c == '\n')
+ ao_debug_out('\r');
+ while (!(stm_usart1.isr & (1 << STM_USART_ISR_TXE)));
+ stm_usart1.tdr = c;
+}
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+ if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+ if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts, foo) == 1) {
+ ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+ return 0;
+ }
+#endif
+ if (usart->reg->isr & (1 << STM_USART_ISR_TXE))
+ {
+ usart->tx_running = 1;
+ usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+ ao_fifo_remove(usart->tx_fifo, usart->reg->tdr);
+ ao_wakeup(&usart->tx_fifo);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+ if (_ao_usart_tx_start(usart))
+ ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int stdin)
+{
+ if (usart->reg->isr & (1 << STM_USART_ISR_RXNE)) {
+ usart->reg->icr = (1 << STM_USART_ICR_ORECF);
+ if (!ao_fifo_full(usart->rx_fifo)) {
+ ao_fifo_insert(usart->rx_fifo, usart->reg->rdr);
+ ao_wakeup(&usart->rx_fifo);
+ if (stdin)
+ ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+ /* If the fifo is nearly full, turn off RTS and wait
+ * for it to drain a bunch
+ */
+ if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, usart->pin_rts, 1);
+ usart->rts = 0;
+ }
+#endif
+ } else {
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_RXNEIE);
+ }
+ }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int stdin)
+{
+ _ao_usart_rx(usart, stdin);
+
+ if (!_ao_usart_tx_start(usart))
+ usart->reg->cr1 &= ~(1<< STM_USART_CR1_TXEIE);
+
+ if (usart->reg->isr & (1 << STM_USART_ISR_TC)) {
+ usart->tx_running = 0;
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_TCIE);
+ if (usart->draining) {
+ usart->draining = 0;
+ ao_wakeup(&usart->tx_fifo);
+ }
+ }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+ int c;
+
+ if (ao_fifo_empty(usart->rx_fifo))
+ c = AO_READ_AGAIN;
+ else {
+ uint8_t u;
+ ao_fifo_remove(usart->rx_fifo,u);
+ if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+ if (ao_fifo_barely(usart->rx_fifo))
+ usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+ }
+#if HAS_SERIAL_SW_FLOW
+ /* If we've cleared RTS, check if there's space now and turn it back on */
+ if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, foo, 0);
+ usart->rts = 1;
+ }
+#endif
+ c = u;
+ }
+ return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+ ao_sleep(&usart->rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
+{
+ return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+ ao_arch_block_interrupts();
+ while (ao_fifo_full(usart->tx_fifo))
+ ao_sleep(&usart->tx_fifo);
+ ao_fifo_insert(usart->tx_fifo, c);
+ _ao_usart_tx_start(usart);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+ ao_arch_block_interrupts();
+ while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+ usart->draining = 1;
+ ao_sleep(&usart->tx_fifo);
+ }
+ ao_arch_release_interrupts();
+}
+
+static const struct {
+ uint32_t brr;
+} ao_usart_speeds[] = {
+ [AO_SERIAL_SPEED_4800] = {
+ AO_PCLK / 4800
+ },
+ [AO_SERIAL_SPEED_9600] = {
+ AO_PCLK / 9600
+ },
+ [AO_SERIAL_SPEED_19200] = {
+ AO_PCLK / 19200
+ },
+ [AO_SERIAL_SPEED_57600] = {
+ AO_PCLK / 57600
+ },
+ [AO_SERIAL_SPEED_115200] = {
+ AO_PCLK / 115200
+ },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+ if (speed > AO_SERIAL_SPEED_115200)
+ return;
+ usart->reg->brr = ao_usart_speeds[speed].brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart)
+{
+ usart->reg->cr1 = ((0 << STM_USART_CR1_M1) |
+ (0 << STM_USART_CR1_EOBIE) |
+ (0 << STM_USART_CR1_RTOIE) |
+ (0 << STM_USART_CR1_DEAT) |
+ (0 << STM_USART_CR1_DEDT) |
+ (0 << STM_USART_CR1_OVER8) |
+ (0 << STM_USART_CR1_CMIE) |
+ (0 << STM_USART_CR1_MME) |
+ (0 << STM_USART_CR1_M0) |
+ (0 << STM_USART_CR1_WAKE) |
+ (0 << STM_USART_CR1_PCE) |
+ (0 << STM_USART_CR1_PS) |
+ (0 << STM_USART_CR1_PEIE) |
+ (0 << STM_USART_CR1_TXEIE) |
+ (0 << STM_USART_CR1_TCIE) |
+ (1 << STM_USART_CR1_RXNEIE) |
+ (0 << STM_USART_CR1_IDLEIE) |
+ (1 << STM_USART_CR1_TE) |
+ (1 << STM_USART_CR1_RE) |
+ (0 << STM_USART_CR1_UESM) |
+ (0 << STM_USART_CR1_UE));
+
+ usart->reg->cr2 = ((0 << STM_USART_CR2_ADD) |
+ (0 << STM_USART_CR2_RTOEN) |
+ (0 << STM_USART_CR2_ABRMOD) |
+ (0 << STM_USART_CR2_ABREN) |
+ (0 << STM_USART_CR2_MSBFIRST) |
+ (0 << STM_USART_CR2_DATAINV) |
+ (0 << STM_USART_CR2_TXINV) |
+ (0 << STM_USART_CR2_RXINV) |
+ (0 << STM_USART_CR2_SWAP) |
+ (0 << STM_USART_CR2_LINEN) |
+ (0 << STM_USART_CR2_STOP) |
+ (0 << STM_USART_CR2_CLKEN) |
+ (0 << STM_USART_CR2_CPOL) |
+ (0 << STM_USART_CR2_CHPA) |
+ (0 << STM_USART_CR2_LBCL) |
+ (0 << STM_USART_CR2_LBDIE) |
+ (0 << STM_USART_CR2_LBDL) |
+ (0 << STM_USART_CR2_ADDM7));
+
+ usart->reg->cr3 = ((0 << STM_USART_CR3_WUFIE) |
+ (0 << STM_USART_CR3_WUS) |
+ (0 << STM_USART_CR3_SCARCNT) |
+ (0 << STM_USART_CR3_DEP) |
+ (0 << STM_USART_CR3_DEM) |
+ (0 << STM_USART_CR3_DDRE) |
+ (0 << STM_USART_CR3_OVRDIS) |
+ (0 << STM_USART_CR3_ONEBIT) |
+ (0 << STM_USART_CR3_CTIIE) |
+ (0 << STM_USART_CR3_CTSE) |
+ (0 << STM_USART_CR3_RTSE) |
+ (0 << STM_USART_CR3_DMAT) |
+ (0 << STM_USART_CR3_DMAR) |
+ (0 << STM_USART_CR3_SCEN) |
+ (0 << STM_USART_CR3_NACK) |
+ (0 << STM_USART_CR3_HDSEL) |
+ (0 << STM_USART_CR3_IRLP) |
+ (0 << STM_USART_CR3_IREN) |
+ (0 << STM_USART_CR3_EIE));
+
+
+ /* Pick a 9600 baud rate */
+ ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+
+ /* Enable the usart */
+ usart->reg->cr1 |= (1 << STM_USART_CR1_UE);
+
+}
+
+#if HAS_SERIAL_HW_FLOW
+static void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+}
+#endif
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart1);
+ ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart2);
+ ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+void
+ao_serial2_cts(void)
+{
+ _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+ void (*isr)(void),
+ struct stm_gpio *port_rts,
+ int pin_rts,
+ struct stm_gpio *port_cts,
+ int pin_cts)
+{
+ /* Pull RTS low to note that there's space in the FIFO
+ */
+ ao_enable_output(port_rts, pin_rts, foo, 0);
+ usart->gpio_rts = port_rts;
+ usart->pin_rts = pin_rts;
+ usart->rts = 1;
+
+ ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+ usart->gpio_cts = port_cts;
+ usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+ /*
+ * TX RX
+ * PA9 PA10
+ * PB6 PB7
+ */
+
+#if SERIAL_1_PA9_PA10
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 9, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 10, STM_AFR_AF1);
+#else
+#if SERIAL_1_PB6_PB7
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPBEN);
+
+ stm_afr_set(&stm_gpiob, 6, STM_AFR_AF0);
+ stm_afr_set(&stm_gpiob, 7, STM_AFR_AF0);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+ ao_stm_usart1.reg = &stm_usart1;
+ ao_usart_init(&ao_stm_usart1);
+
+ stm_nvic_set_enable(STM_ISR_USART1_POS);
+ stm_nvic_set_priority(STM_ISR_USART1_POS, 4);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+ ao_add_stdio(_ao_serial1_pollchar,
+ ao_serial1_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+ /*
+ * TX RX
+ * PA2 PA3
+ * PA14 PA15
+ */
+
+# if SERIAL_2_PA2_PA3
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 2, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 3, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# if USE_SERIAL_2_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+ ao_serial2_cts,
+ SERIAL_2_PORT_RTS,
+ SERIAL_2_PIN_RTS,
+ SERIAL_2_PORT_CTS,
+ SERIAL_2_PIN_CTS);
+# else
+ stm_afr_set(&stm_gpioa, 0, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF1);
+# endif
+# endif
+# else
+# if SERIAL_2_PA14_PA15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 14, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# error "Don't know how to set flowcontrol for serial 2 on PA14"
+# endif
+# else
+# if SERIAL_2_PA2_PA15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 2, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# error "Don't know how to set flowcontrol for serial 2 on PA2_PA15"
+# endif
+# else
+# error "No SERIAL_2 port configuration specified"
+# endif
+# endif
+# endif
+ /* Enable USART */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+ ao_stm_usart2.reg = &stm_usart2;
+ ao_usart_init(&ao_stm_usart2);
+# if USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW
+ ao_usart_set_flow(&ao_stm_usart2);
+# endif
+
+ stm_nvic_set_enable(STM_ISR_USART2_POS);
+ stm_nvic_set_priority(STM_ISR_USART2_POS, 4);
+# if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+ ao_add_stdio(_ao_serial2_pollchar,
+ ao_serial2_putchar,
+ NULL);
+# endif
+#endif
+}
diff --git a/src/stmf0/ao_spi_stm.c b/src/stmf0/ao_spi_stm.c
index 0448ad8c..5e76d6c3 100644
--- a/src/stmf0/ao_spi_stm.c
+++ b/src/stmf0/ao_spi_stm.c
@@ -536,12 +536,18 @@ void
ao_spi_init(void)
{
#if HAS_SPI_1
+#ifndef SPI_1_PA5_PA6_PA7
+#error SPI_1_PA5_PA6_PA7 undefined
+#endif
# if SPI_1_PA5_PA6_PA7
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
# endif
+# ifndef SPI_1_PB3_PB4_PB5
+# error SPI_1_PB3_PB4_PB5 undefined
+# endif
# if SPI_1_PB3_PB4_PB5
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPBEN);
stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
diff --git a/src/stmf0/ao_spi_stm_slave.c b/src/stmf0/ao_spi_stm_slave.c
new file mode 100644
index 00000000..962ff2c6
--- /dev/null
+++ b/src/stmf0/ao_spi_stm_slave.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_spi_stm_slave_info {
+ uint8_t miso_dma_index;
+ uint8_t mosi_dma_index;
+ struct stm_spi *stm_spi;
+};
+
+static uint8_t ao_spi_slave_mutex[STM_NUM_SPI];
+static uint8_t ao_spi_slave_index[STM_NUM_SPI];
+
+static const struct ao_spi_stm_slave_info ao_spi_stm_slave_info[STM_NUM_SPI] = {
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+ &stm_spi1
+ },
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+ &stm_spi2
+ }
+};
+
+static uint8_t spi_dev_null;
+
+void
+ao_spi_slave_send(void *block, uint16_t len)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
+ uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].miso_dma_index;
+
+ /* Set up the transmit DMA to deliver data */
+ ao_dma_set_transfer(mosi_dma_index,
+ &stm_spi->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+ /* Clear RXNE */
+ (void) stm_spi->dr;
+
+ /* Set up the receive DMA -- when this is done, we know the SPI unit
+ * is idle. Without this, we'd have to poll waiting for the BSY bit to
+ * be cleared
+ */
+ ao_dma_set_transfer(miso_dma_index,
+ &stm_spi->dr,
+ &spi_dev_null,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (0 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (1 << STM_SPI_CR2_RXDMAEN));
+ ao_dma_start(miso_dma_index);
+ ao_dma_start(mosi_dma_index);
+ ao_arch_critical(
+ while (!ao_dma_done[miso_dma_index])
+ ao_sleep(&ao_dma_done[miso_dma_index]);
+ );
+ ao_dma_done_transfer(mosi_dma_index);
+ ao_dma_done_transfer(miso_dma_index);
+}
+
+uint8_t
+ao_spi_slave_recv(void *block, uint16_t len)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
+ uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].miso_dma_index;
+
+ /* Set up transmit DMA to make the SPI hardware actually run */
+ ao_dma_set_transfer(mosi_dma_index,
+ &stm_spi->dr,
+ &spi_dev_null,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (0 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+ /* Clear RXNE */
+ (void) stm_spi->dr;
+
+ /* Set up the receive DMA to capture data */
+ ao_dma_set_transfer(miso_dma_index,
+ &stm_spi->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (1 << STM_SPI_CR2_RXDMAEN));
+ ao_dma_start(miso_dma_index);
+ ao_dma_start(mosi_dma_index);
+
+ /* Wait until the SPI unit is done */
+ ao_arch_critical(
+ while (!ao_dma_done[miso_dma_index])
+ ao_sleep(&ao_dma_done[miso_dma_index]);
+ );
+
+ ao_dma_done_transfer(mosi_dma_index);
+ ao_dma_done_transfer(miso_dma_index);
+ return 1;
+}
+
+static void
+ao_spi_slave_disable_index(uint8_t spi_index)
+{
+ /* Disable current config
+ */
+ switch (AO_SPI_INDEX(spi_index)) {
+ case STM_SPI_INDEX(1):
+ switch (spi_index) {
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_gpio_set(&stm_gpioa, 5, 1);
+ stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_gpio_set(&stm_gpiob, 3, 1);
+ stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_1_PE13_PE14_PE15:
+ stm_gpio_set(&stm_gpioe, 13, 1);
+ stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
+ break;
+ }
+ break;
+ case STM_SPI_INDEX(2):
+ switch (spi_index) {
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_gpio_set(&stm_gpiob, 13, 1);
+ stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_2_PD1_PD3_PD4:
+ stm_gpio_set(&stm_gpiod, 1, 1);
+ stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
+ break;
+ }
+ break;
+ }
+}
+
+static void
+ao_spi_slave_enable_index(uint8_t spi_index)
+{
+ switch (AO_SPI_INDEX(spi_index)) {
+ case STM_SPI_INDEX(1):
+ switch (spi_index) {
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
+ break;
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
+ break;
+ case AO_SPI_1_PE13_PE14_PE15:
+ stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
+ break;
+ }
+ break;
+ case STM_SPI_INDEX(2):
+ switch (spi_index) {
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
+ break;
+ case AO_SPI_2_PD1_PD3_PD4:
+ stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
+ break;
+ }
+ break;
+ }
+}
+
+void
+ao_spi_slave_get(uint8_t spi_index, uint32_t speed)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ ao_mutex_get(&ao_spi_slave_mutex[id]);
+ stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) | /* Three wire mode */
+ (0 << STM_SPI_CR1_BIDIOE) |
+ (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
+ (0 << STM_SPI_CR1_CRCNEXT) |
+ (0 << STM_SPI_CR1_DFF) |
+ (0 << STM_SPI_CR1_RXONLY) |
+ (1 << STM_SPI_CR1_SSM) | /* Software SS handling */
+ (1 << STM_SPI_CR1_SSI) | /* ... */
+ (0 << STM_SPI_CR1_LSBFIRST) | /* Big endian */
+ (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
+ (speed << STM_SPI_CR1_BR) | /* baud rate to pclk/4 */
+ (1 << STM_SPI_CR1_MSTR) |
+ (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
+ (0 << STM_SPI_CR1_CPHA));
+ if (spi_index != ao_spi_slave_index[id]) {
+
+ /* Disable old config
+ */
+ ao_spi_slave_disable_index(ao_spi_slave_index[id]);
+
+ /* Enable new config
+ */
+ ao_spi_slave_enable_index(spi_index);
+
+ /* Remember current config
+ */
+ ao_spi_slave_index[id] = spi_index;
+ }
+}
+
+void
+ao_spi_slave_put(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ stm_spi->cr1 = 0;
+ ao_mutex_put(&ao_spi_slave_mutex[id]);
+}
+
+static void
+ao_spi_channel_init(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ ao_spi_slave_disable_index(spi_index);
+
+ stm_spi->cr1 = 0;
+ (void) stm_spi->sr;
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (0 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+}
+
+void
+ao_spi_slave_init(void)
+{
+#if HAS_SPI_SLAVE_1
+# if SPI_1_PA5_PA6_PA7
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+# endif
+# if SPI_1_PB3_PB4_PB5
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_1_PE13_PE14_PE15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
+# endif
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+ ao_spi_slave_index[0] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_SLAVE_2
+# if SPI_2_PB13_PB14_PB15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_2_PD1_PD3_PD4
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+# endif
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+ ao_spi_slave_index[1] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(1);
+#endif
+}
diff --git a/src/stmf0/stm32f0.h b/src/stmf0/stm32f0.h
index bafa763a..e53a5dfd 100644
--- a/src/stmf0/stm32f0.h
+++ b/src/stmf0/stm32f0.h
@@ -1812,15 +1812,15 @@ extern struct stm_tim23 stm_tim2, stm_tim3;
#define STM_TIM23_CCMR2_OC4CE 15
#define STM_TIM23_CCMR2_OC4M 12
-#define STM_TIM23_CCMR2_OCM_FROZEN 0
-#define STM_TIM23_CCMR2_OCM_SET_HIGH_ON_MATCH 1
-#define STM_TIM23_CCMR2_OCM_SET_LOW_ON_MATCH 2
-#define STM_TIM23_CCMR2_OCM_TOGGLE 3
-#define STM_TIM23_CCMR2_OCM_FORCE_LOW 4
-#define STM_TIM23_CCMR2_OCM_FORCE_HIGH 5
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_1 6
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_2 7
-#define STM_TIM23_CCMR2_OCM_MASK 7
+#define STM_TIM23_CCMR2_OC4M_FROZEN 0
+#define STM_TIM23_CCMR2_OC4M_SET_HIGH_ON_MATCH 1
+#define STM_TIM23_CCMR2_OC4M_SET_LOW_ON_MATCH 2
+#define STM_TIM23_CCMR2_OC4M_TOGGLE 3
+#define STM_TIM23_CCMR2_OC4M_FORCE_LOW 4
+#define STM_TIM23_CCMR2_OC4M_FORCE_HIGH 5
+#define STM_TIM23_CCMR2_OC4M_PWM_MODE_1 6
+#define STM_TIM23_CCMR2_OC4M_PWM_MODE_2 7
+#define STM_TIM23_CCMR2_OC4M_MASK 7
#define STM_TIM23_CCMR2_OC4PE 11
#define STM_TIM23_CCMR2_OC4FE 10
#define STM_TIM23_CCMR2_CC4S 8
@@ -1832,15 +1832,15 @@ extern struct stm_tim23 stm_tim2, stm_tim3;
#define STM_TIM23_CCMR2_OC3CE 7
#define STM_TIM23_CCMR2_OC3M 4
-#define STM_TIM23_CCMR2_OCM_FROZEN 0
-#define STM_TIM23_CCMR2_OCM_SET_HIGH_ON_MATCH 1
-#define STM_TIM23_CCMR2_OCM_SET_LOW_ON_MATCH 2
-#define STM_TIM23_CCMR2_OCM_TOGGLE 3
-#define STM_TIM23_CCMR2_OCM_FORCE_LOW 4
-#define STM_TIM23_CCMR2_OCM_FORCE_HIGH 5
+#define STM_TIM23_CCMR2_OC3M_FROZEN 0
+#define STM_TIM23_CCMR2_OC3M_SET_HIGH_ON_MATCH 1
+#define STM_TIM23_CCMR2_OC3M_SET_LOW_ON_MATCH 2
+#define STM_TIM23_CCMR2_OC3M_TOGGLE 3
+#define STM_TIM23_CCMR2_OC3M_FORCE_LOW 4
+#define STM_TIM23_CCMR2_OC3M_FORCE_HIGH 5
#define STM_TIM23_CCMR2_OC3M_PWM_MODE_1 6
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_2 7
-#define STM_TIM23_CCMR2_OCM_MASK 7
+#define STM_TIM23_CCMR2_OC3M_PWM_MODE_2 7
+#define STM_TIM23_CCMR2_OC3M_MASK 7
#define STM_TIM23_CCMR2_OC3PE 11
#define STM_TIM23_CCMR2_OC3FE 2
#define STM_TIM23_CCMR2_CC3S 0
@@ -2010,4 +2010,129 @@ struct stm_exti {
extern struct stm_exti stm_exti;
+struct stm_usart {
+ vuint32_t cr1; /* control register 1 */
+ vuint32_t cr2; /* control register 2 */
+ vuint32_t cr3; /* control register 3 */
+ vuint32_t brr; /* baud rate register */
+
+ vuint32_t gtpr; /* guard time and prescaler */
+ vuint32_t rtor; /* receiver timeout register */
+ vuint32_t rqr; /* request register */
+ vuint32_t isr; /* interrupt and status register */
+
+ vuint32_t icr; /* interrupt flag clear register */
+ vuint32_t rdr; /* receive data register */
+ vuint32_t tdr; /* transmit data register */
+};
+
+#define STM_USART_CR1_M1 28
+#define STM_USART_CR1_EOBIE 27
+#define STM_USART_CR1_RTOIE 26
+#define STM_USART_CR1_DEAT 21
+#define STM_USART_CR1_DEDT 16
+#define STM_USART_CR1_OVER8 15
+#define STM_USART_CR1_CMIE 14
+#define STM_USART_CR1_MME 13
+#define STM_USART_CR1_M0 12
+#define STM_USART_CR1_WAKE 11
+#define STM_USART_CR1_PCE 10
+#define STM_USART_CR1_PS 9
+#define STM_USART_CR1_PEIE 8
+#define STM_USART_CR1_TXEIE 7
+#define STM_USART_CR1_TCIE 6
+#define STM_USART_CR1_RXNEIE 5
+#define STM_USART_CR1_IDLEIE 4
+#define STM_USART_CR1_TE 3
+#define STM_USART_CR1_RE 2
+#define STM_USART_CR1_UESM 1
+#define STM_USART_CR1_UE 0
+
+#define STM_USART_CR2_ADD 24
+#define STM_USART_CR2_RTOEN 23
+#define STM_USART_CR2_ABRMOD 21
+#define STM_USART_CR2_ABREN 20
+#define STM_USART_CR2_MSBFIRST 19
+#define STM_USART_CR2_DATAINV 18
+#define STM_USART_CR2_TXINV 17
+#define STM_USART_CR2_RXINV 16
+#define STM_USART_CR2_SWAP 15
+#define STM_USART_CR2_LINEN 14
+#define STM_USART_CR2_STOP 12
+#define STM_USART_CR2_CLKEN 11
+#define STM_USART_CR2_CPOL 10
+#define STM_USART_CR2_CHPA 9
+#define STM_USART_CR2_LBCL 8
+#define STM_USART_CR2_LBDIE 6
+#define STM_USART_CR2_LBDL 5
+#define STM_USART_CR2_ADDM7 4
+
+#define STM_USART_CR3_WUFIE 22
+#define STM_USART_CR3_WUS 20
+#define STM_USART_CR3_SCARCNT 17
+#define STM_USART_CR3_DEP 15
+#define STM_USART_CR3_DEM 14
+#define STM_USART_CR3_DDRE 13
+#define STM_USART_CR3_OVRDIS 12
+#define STM_USART_CR3_ONEBIT 11
+#define STM_USART_CR3_CTIIE 10
+#define STM_USART_CR3_CTSE 9
+#define STM_USART_CR3_RTSE 8
+#define STM_USART_CR3_DMAT 7
+#define STM_USART_CR3_DMAR 6
+#define STM_USART_CR3_SCEN 5
+#define STM_USART_CR3_NACK 4
+#define STM_USART_CR3_HDSEL 3
+#define STM_USART_CR3_IRLP 2
+#define STM_USART_CR3_IREN 1
+#define STM_USART_CR3_EIE 0
+
+#define STM_USART_GTPR_GT 8
+#define STM_USART_GTPR_PSC 0
+
+#define STM_USART_RQR_TXFRQ 4
+#define STM_USART_RQR_RXFRQ 3
+#define STM_USART_RQR_MMRQ 2
+#define STM_USART_RQR_SBKRQ 1
+#define STM_USART_RQR_ABRRQ 0
+
+#define STM_USART_ISR_REACK 22
+#define STM_USART_ISR_TEACK 21
+#define STM_USART_ISR_WUF 20
+#define STM_USART_ISR_RWU 19
+#define STM_USART_ISR_SBKF 18
+#define STM_USART_ISR_CMF 17
+#define STM_USART_ISR_BUSY 16
+#define STM_USART_ISR_ABRF 15
+#define STM_USART_ISR_ABRE 14
+#define STM_USART_ISR_EOBF 12
+#define STM_USART_ISR_RTOF 11
+#define STM_USART_ISR_CTS 10
+#define STM_USART_ISR_CTSIF 9
+#define STM_USART_ISR_LBDF 8
+#define STM_USART_ISR_TXE 7
+#define STM_USART_ISR_TC 6
+#define STM_USART_ISR_RXNE 5
+#define STM_USART_ISR_IDLE 4
+#define STM_USART_ISR_ORE 3
+#define STM_USART_ISR_NF 2
+#define STM_USART_ISR_FE 1
+#define STM_USART_ISR_PE 0
+
+#define STM_USART_ICR_WUCF 20
+#define STM_USART_ICR_CMCF 17
+#define STM_USART_ICR_EOBCF 12
+#define STM_USART_ICR_RTOCF 11
+#define STM_USART_ICR_CTSCF 9
+#define STM_USART_ICR_LBDCF 8
+#define STM_USART_ICR_TCCF 6
+#define STM_USART_ICR_IDLECF 4
+#define STM_USART_ICR_ORECF 3
+#define STM_USART_ICR_NCF 2
+#define STM_USART_ICR_FECF 1
+#define STM_USART_ICR_PECF 0
+
+extern struct stm_usart stm_usart1;
+extern struct stm_usart stm_usart2;
+
#endif /* _STM32F0_H_ */
diff --git a/src/telebt-v3.0/Makefile b/src/telebt-v3.0/Makefile
index a7ef4d64..40d1f6e4 100644
--- a/src/telebt-v3.0/Makefile
+++ b/src/telebt-v3.0/Makefile
@@ -58,7 +58,12 @@ ALTOS_SRC = \
ao_monitor.c \
$(PROFILE) \
$(SAMPLE_PROFILE) \
- $(STACK_GUARD)
+ $(STACK_GUARD) \
+ ao_lco_func.c \
+ ao_radio_cmac.c \
+ ao_aes.c \
+ ao_aes_tables.c \
+ ao_lco_cmd.c
PRODUCT=TeleBT-v3.0
PRODUCT_DEF=-DTELEBT_V_3_0
diff --git a/src/telebt-v3.0/ao_pins.h b/src/telebt-v3.0/ao_pins.h
index 61cbe9bb..62dddf2a 100644
--- a/src/telebt-v3.0/ao_pins.h
+++ b/src/telebt-v3.0/ao_pins.h
@@ -80,6 +80,7 @@
#define HAS_TELEMETRY 0
#define HAS_APRS 0
#define HAS_ACCEL 0
+#define HAS_AES 1
#define HAS_SPI_1 1
#define SPI_1_PA5_PA6_PA7 1 /* CC1200 */
@@ -197,6 +198,7 @@ struct ao_adc {
#define AO_CC1200_SPI_CS_PIN 10
#define AO_CC1200_SPI_BUS AO_SPI_1_PA5_PA6_PA7
#define AO_CC1200_SPI stm_spi1
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpiob)
#define AO_CC1200_INT_PIN (11)
diff --git a/src/telebt-v3.0/ao_telebt.c b/src/telebt-v3.0/ao_telebt.c
index 9117863b..8775d993 100644
--- a/src/telebt-v3.0/ao_telebt.c
+++ b/src/telebt-v3.0/ao_telebt.c
@@ -22,6 +22,7 @@
#include <ao_eeprom.h>
#include <ao_profile.h>
#include <ao_btm.h>
+#include <ao_lco_cmd.h>
#if HAS_SAMPLE_PROFILE
#include <ao_sample_profile.h>
#endif
@@ -44,6 +45,8 @@ main(void)
ao_btm_init();
ao_cmd_init();
+ ao_lco_cmd_init();
+
ao_eeprom_init();
ao_usb_init();
diff --git a/src/teledongle-v3.0/ao_pins.h b/src/teledongle-v3.0/ao_pins.h
index effc2322..be710aef 100644
--- a/src/teledongle-v3.0/ao_pins.h
+++ b/src/teledongle-v3.0/ao_pins.h
@@ -96,6 +96,7 @@
#define AO_CC1200_SPI_CS_PIN 3
#define AO_CC1200_SPI_BUS 0
#define AO_CC1200_SPI 0
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_8MHz
#define AO_CC1200_INT_PORT 0
#define AO_CC1200_INT_PIN 2
diff --git a/src/telefiretwo-v0.1/ao_pins.h b/src/telefiretwo-v0.1/ao_pins.h
index f56061b2..1e5c0d09 100644
--- a/src/telefiretwo-v0.1/ao_pins.h
+++ b/src/telefiretwo-v0.1/ao_pins.h
@@ -110,6 +110,7 @@
#define AO_CC1200_SPI_CS_PIN 7
#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpiob)
#define AO_CC1200_INT_PIN (11)
diff --git a/src/telefiretwo-v0.2/ao_pins.h b/src/telefiretwo-v0.2/ao_pins.h
index 70af5dd1..469e9937 100644
--- a/src/telefiretwo-v0.2/ao_pins.h
+++ b/src/telefiretwo-v0.2/ao_pins.h
@@ -110,6 +110,7 @@
#define AO_CC1200_SPI_CS_PIN 7
#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpiob)
#define AO_CC1200_INT_PIN (11)
diff --git a/src/telefiretwo-v1.0/Makefile b/src/telefiretwo-v1.0/Makefile
new file mode 100644
index 00000000..87d5d477
--- /dev/null
+++ b/src/telefiretwo-v1.0/Makefile
@@ -0,0 +1,95 @@
+#
+# TeleFire build file
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_pins.h \
+ ao_log.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pad.h \
+ ao_product.h \
+ ao_radio_spi.h \
+ ao_radio_cmac.h \
+ ao_cc1200_CC1200.h \
+ ao_cc1200.h \
+ stm32l.h
+#
+# Common AltOS sources
+#
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_adc_stm.c \
+ ao_data.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_beep_stm.c \
+ ao_eeprom_stm.c \
+ ao_storage.c \
+ ao_m25.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_cc1200.c \
+ ao_radio_cmac.c \
+ ao_aes.c \
+ ao_aes_tables.c \
+ ao_pad.c \
+ ao_radio_cmac_cmd.c \
+ ao_log.c \
+ ao_log_firetwo.c
+
+PRODUCT_SRC = \
+ ao_telefiretwo.c
+
+PRODUCT=TeleFireTwo-v1.0
+PRODUCT_DEF=-DTELEFIRETWO_V_1_0
+IDPRODUCT=0x000f
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
+
+PROGNAME = telefiretwo-v1.0
+PROG = $(PROGNAME)-$(VERSION).elf
+HEX = $(PROGNAME)-$(VERSION).ihx
+
+SRC = $(ALTOS_SRC) $(PRODUCT_SRC)
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
+
diff --git a/src/telefiretwo-v1.0/ao_pins.h b/src/telefiretwo-v1.0/ao_pins.h
new file mode 100644
index 00000000..b2f5a5ab
--- /dev/null
+++ b/src/telefiretwo-v1.0/ao_pins.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO 1
+#define HAS_RADIO_RATE 1
+#define HAS_TELEMETRY 0
+
+#define HAS_FLIGHT 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define BEEPER_CHANNEL 4
+#define HAS_GPS 0
+#define HAS_SERIAL_1 0
+#define HAS_ADC 1
+#define HAS_DBG 0
+#define HAS_EEPROM 1
+#define HAS_LOG 1
+#define HAS_PAD 1
+#define USE_INTERNAL_FLASH 0
+#define IGNITE_ON_P0 0
+#define PACKET_HAS_MASTER 0
+#define PACKET_HAS_SLAVE 0
+#define AO_DATA_RING 32
+#define HAS_FIXED_PAD_BOX 1
+
+#define LOG_ERASE_MARK 0x55
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+#define AO_CC1200_FOSC 40000000
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_EEPROM 1
+#define USE_EEPROM_CONFIG 1
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_RADIO 1
+#define HAS_RADIO_RATE 1
+#define HAS_TELEMETRY 0
+#define HAS_AES 1
+
+#define HAS_SPI_1 0
+#define SPI_1_PA5_PA6_PA7 0
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+
+#define HAS_SPI_2 1 /* CC1200 */
+#define SPI_2_PB13_PB14_PB15 1
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_GPIO (&stm_gpiob)
+#define SPI_2_SCK 13
+#define SPI_2_MISO 14
+#define SPI_2_MOSI 15
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1 0
+
+#define HAS_I2C_2 0
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 0
+
+#define FAST_TIMER_FREQ 10000 /* .1ms for debouncing */
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpioa)
+#define AO_M25_SPI_CS_MASK (1 << 15)
+#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio is a cc1200 connected via SPI
+ */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpioa)
+#define AO_CC1200_SPI_CS_PIN 7
+#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
+
+#define AO_CC1200_INT_PORT (&stm_gpiob)
+#define AO_CC1200_INT_PIN (11)
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define LED_PORT_0 (&stm_gpioa)
+#define LED_PORT_1 (&stm_gpiob)
+
+#define LED_PORT_0_ENABLE STM_RCC_AHBENR_GPIOAEN
+#define LED_PORT_1_ENABLE STM_RCC_AHBENR_GPIOBEN
+
+/* Port A, pins 4-6 */
+#define LED_PORT_0_SHIFT 4
+#define LED_PORT_0_MASK 0x7
+#define LED_PIN_GREEN 0
+#define LED_PIN_AMBER 1
+#define LED_PIN_RED 2
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_AMBER (1 << LED_PIN_AMBER)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+/* Port B, pins 4-5 */
+#define LED_PORT_1_SHIFT 0
+#define LED_PORT_1_MASK (0x3 << 4)
+#define LED_PIN_CONT_0 4
+#define LED_PIN_ARMED 5
+
+#define AO_LED_ARMED (1 << LED_PIN_ARMED)
+#define AO_LED_CONTINUITY(c) (1 << (4 - (c)))
+#define AO_LED_CONTINUITY_MASK (0x1 << 4)
+
+#define LEDS_AVAILABLE (LED_PORT_0_MASK|LED_PORT_1_MASK)
+
+/* Alarm A */
+#define AO_SIREN
+#define AO_SIREN_PORT (&stm_gpiob)
+#define AO_SIREN_PIN 8
+
+/* Alarm B */
+#define AO_STROBE
+#define AO_STROBE_PORT (&stm_gpiob)
+#define AO_STROBE_PIN 9
+
+#define SPI_CONST 0x00
+
+#define AO_PAD_NUM 1
+#define AO_PAD_PORT (&stm_gpioa)
+
+#define AO_PAD_PIN_0 1
+#define AO_PAD_ADC_0 0
+
+#define AO_PAD_ALL_PINS ((1 << AO_PAD_PIN_0))
+#define AO_PAD_ALL_CHANNELS ((1 << 0))
+
+/* test these values with real igniters */
+#define AO_PAD_RELAY_CLOSED 3524
+#define AO_PAD_NO_IGNITER 16904
+#define AO_PAD_GOOD_IGNITER 22514
+
+#define AO_PAD_ADC_PYRO 2
+#define AO_PAD_ADC_BATT 8
+
+#define AO_PAD_ADC_THRUST 3
+#define AO_PAD_ADC_PRESSURE 18
+
+#define AO_ADC_FIRST_PIN 0
+
+#define AO_NUM_ADC 5
+
+#define AO_ADC_SQ1 AO_PAD_ADC_0
+#define AO_ADC_SQ2 AO_PAD_ADC_PYRO
+#define AO_ADC_SQ3 AO_PAD_ADC_BATT
+#define AO_ADC_SQ4 AO_PAD_ADC_THRUST
+#define AO_ADC_SQ5 AO_PAD_ADC_PRESSURE
+
+#define AO_PYRO_R_PYRO_SENSE 200
+#define AO_PYRO_R_SENSE_GND 22
+
+#define AO_FIRE_R_POWER_FET 0
+#define AO_FIRE_R_FET_SENSE 200
+#define AO_FIRE_R_SENSE_GND 22
+
+#define HAS_ADC_TEMP 0
+
+struct ao_adc {
+ int16_t sense[AO_PAD_NUM];
+ int16_t pyro;
+ int16_t batt;
+ int16_t thrust;
+ int16_t pressure;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf ("tick: %5u 0: %5d pyro: %5d batt %5d thrust %5d pressure %5d\n", \
+ (p)->tick, \
+ (p)->adc.sense[0], \
+ (p)->adc.pyro, \
+ (p)->adc.batt, \
+ (p)->adc.thrust, \
+ (p)->adc.pressure)
+
+#define AO_ADC_PINS ((1 << AO_PAD_ADC_0) | \
+ (1 << AO_PAD_ADC_PYRO) | \
+ (1 << AO_PAD_ADC_BATT) | \
+ (1 << AO_PAD_ADC_THRUST) | \
+ (1 << AO_PAD_ADC_PRESSURE))
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telefiretwo-v1.0/ao_telefiretwo.c b/src/telefiretwo-v1.0/ao_telefiretwo.c
new file mode 100644
index 00000000..115b3e91
--- /dev/null
+++ b/src/telefiretwo-v1.0/ao_telefiretwo.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_log.h>
+#include <ao_pad.h>
+#include <ao_exti.h>
+#include <ao_radio_cmac_cmd.h>
+#include <ao_eeprom.h>
+
+static void
+set_logging(void)
+{
+ ao_cmd_hex();
+ ao_log_running = ao_cmd_lex_i;
+ ao_wakeup(&ao_log_running);
+}
+
+__code struct ao_cmds ao_firetwo_cmds[] = {
+ { set_logging, "L <0 off, 1 on>\0Log sensors to flash" },
+ { 0, NULL },
+};
+
+void
+main(void)
+{
+ ao_clock_init();
+
+ ao_led_init(LEDS_AVAILABLE);
+
+ ao_task_init();
+
+ ao_timer_init();
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_cmd_register(&ao_firetwo_cmds[0]);
+ ao_cmd_init();
+
+ ao_adc_init();
+
+ ao_eeprom_init();
+ ao_storage_init();
+ ao_log_init();
+
+ ao_radio_init();
+
+ ao_usb_init();
+
+ ao_config_init();
+
+ ao_pad_init();
+
+// ao_radio_cmac_cmd_init();
+
+ ao_start_scheduler();
+}
diff --git a/src/telefiretwo-v1.0/flash-loader/.gitignore b/src/telefiretwo-v1.0/flash-loader/.gitignore
new file mode 100644
index 00000000..65fe7eab
--- /dev/null
+++ b/src/telefiretwo-v1.0/flash-loader/.gitignore
@@ -0,0 +1,2 @@
+*.elf
+*.ihx
diff --git a/src/telefiretwo-v1.0/flash-loader/Makefile b/src/telefiretwo-v1.0/flash-loader/Makefile
new file mode 100644
index 00000000..d429dcc4
--- /dev/null
+++ b/src/telefiretwo-v1.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telefiretwo-v1.0
+include $(TOPDIR)/stm/Makefile-flash.defs
diff --git a/src/telefiretwo-v1.0/flash-loader/ao_pins.h b/src/telefiretwo-v1.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..ded45a40
--- /dev/null
+++ b/src/telefiretwo-v1.0/flash-loader/ao_pins.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 8MHz */
+#define AO_HSE 8000000
+
+#include <ao_flash_stm_pins.h>
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpiob
+#define AO_BOOT_APPLICATION_PIN 6
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telegps-v0.1/ao_pins.h b/src/telegps-v0.1/ao_pins.h
index 96e8cfd7..5afc9498 100644
--- a/src/telegps-v0.1/ao_pins.h
+++ b/src/telegps-v0.1/ao_pins.h
@@ -148,6 +148,7 @@
#define AO_CC115L_SPI_CS_PIN 12
#define AO_CC115L_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC115L_SPI stm_spi2
+#define AO_CC115L_SPI_SPEED AO_SPI_SPEED_4MHz
#define AO_CC115L_FIFO_INT_GPIO_IOCFG CC115L_IOCFG2
#define AO_CC115L_FIFO_INT_PORT (&stm_gpioa)
@@ -183,4 +184,8 @@
#define AO_SDCARD_SPI_CS_PIN 4
#define AO_SDCARD_SPI stm_spi1
+#define SDCARD_DEBUG 1
+#define SDCARD_WARN 1
+#define SDCARD_TRACE 1
+
#endif /* _AO_PINS_H_ */
diff --git a/src/telegps-v0.1/ao_telegps.c b/src/telegps-v0.1/ao_telegps.c
index eb8ab72d..f734d90f 100644
--- a/src/telegps-v0.1/ao_telegps.c
+++ b/src/telegps-v0.1/ao_telegps.c
@@ -49,16 +49,16 @@ main(void)
ao_cmd_init();
ao_usb_init();
- ao_radio_init();
+// ao_radio_init();
ao_fat_init();
- ao_gps_init();
- ao_gps_report_mega_init();
+// ao_gps_init();
+// ao_gps_report_mega_init();
- ao_telemetry_init();
- ao_telemetry_set_interval(AO_SEC_TO_TICKS(1));
- ao_rdf_set(1);
+// ao_telemetry_init();
+// ao_telemetry_set_interval(AO_SEC_TO_TICKS(1));
+// ao_rdf_set(1);
#if HAS_SAMPLE_PROFILE
ao_sample_profile_init();
diff --git a/src/telegps-v0.3/ao_pins.h b/src/telegps-v0.3/ao_pins.h
index 9c650cc4..28ae30a4 100644
--- a/src/telegps-v0.3/ao_pins.h
+++ b/src/telegps-v0.3/ao_pins.h
@@ -95,6 +95,7 @@
#define AO_CC115L_SPI_CS_PORT 0
#define AO_CC115L_SPI_CS_PIN 3
#define AO_CC115L_SPI_BUS 0
+#define AO_CC115L_SPI_SPEED AO_SPI_SPEED_6MHz
#define AO_CC115L_FIFO_INT_GPIO_IOCFG CC115L_IOCFG2
#define AO_CC115L_FIFO_INT_PORT 0
diff --git a/src/telegps-v1.0/ao_pins.h b/src/telegps-v1.0/ao_pins.h
index 19774f63..9672ab03 100644
--- a/src/telegps-v1.0/ao_pins.h
+++ b/src/telegps-v1.0/ao_pins.h
@@ -97,6 +97,7 @@
#define AO_CC115L_SPI_CS_PORT 0
#define AO_CC115L_SPI_CS_PIN 3
#define AO_CC115L_SPI_BUS 0
+#define AO_CC115L_SPI_SPEED AO_SPI_SPEED_6MHz
#define AO_CC115L_FIFO_INT_GPIO_IOCFG CC115L_IOCFG2
#define AO_CC115L_FIFO_INT_PORT 0
diff --git a/src/telegps-v2.0/Makefile b/src/telegps-v2.0/Makefile
new file mode 100644
index 00000000..19d088d3
--- /dev/null
+++ b/src/telegps-v2.0/Makefile
@@ -0,0 +1,89 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_tracker.h \
+ ao_task.h \
+ ao_cc1200.h \
+ ao_fec.h \
+ stm32f0.h \
+ Makefile
+
+
+ALTOS_SRC = \
+ ao_adc_stm.c \
+ ao_led.c \
+ ao_interrupt.c \
+ ao_boot_chain.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_serial_stm.c \
+ ao_gps_ublox.c \
+ ao_gps_show.c \
+ ao_cc1200.c \
+ ao_aprs.c \
+ ao_tracker.c \
+ ao_telemetry.c \
+ ao_storage.c \
+ ao_m25.c \
+ ao_log.c \
+ ao_log_gps.c \
+ ao_distance.c \
+ ao_sqrt.c \
+ ao_data.c \
+ ao_convert_volt.c \
+ $(SAMPLE_PROFILE)
+
+PRODUCT=TeleGPS-v2.0
+PRODUCT_DEF=-DTELEGPS
+IDPRODUCT=0x0025
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) $(PROFILE_DEF) -g -Os
+
+PROGNAME=telegps-v2.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telegps.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o ao_serial_stm.h $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telegps-v2.0/ao_pins.h b/src/telegps-v2.0/ao_pins.h
new file mode 100644
index 00000000..c51f0afb
--- /dev/null
+++ b/src/telegps-v2.0/ao_pins.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2017 Bdale Garbee <bdale@gag.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPBEN
+#define LED_PORT (&stm_gpiob)
+#define LED_PIN_RED 5
+#define AO_LED_RED (1 << LED_PIN_RED)
+
+#define LEDS_AVAILABLE (AO_LED_RED)
+
+#define IS_FLASH_LOADER 0
+#define HAS_BEEP 0
+
+#define AO_HSE 32000000
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_3
+#define AO_PLLMUL 3
+#define AO_PLLDIV 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define AO_RCC_CFGR2_PLLDIV STM_RCC_CFGR2_PREDIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+
+#define IS_FLASH_LOADER 0
+
+/* ADC */
+
+#define HAS_ADC 1
+#define AO_ADC_PIN0_PORT (&stm_gpioa)
+#define AO_ADC_PIN0_PIN 0
+#define AO_ADC_PIN0_CH 0
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_IOPAEN))
+
+#define AO_NUM_ADC 1
+
+#define AO_DATA_RING 4
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 56 /* 5.6k */
+#define AO_BATTERY_DIV_MINUS 100 /* 10k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
+
+struct ao_adc {
+ int16_t v_batt;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5u batt: %5d\n", \
+ (p)->tick, \
+ (p)->adc.v_batt)
+
+/* SPI */
+#define HAS_SPI_1 1
+#define HAS_SPI_2 0
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_OSPEEDR STM_OSPEEDR_HIGH
+
+#define HAS_MS5607 0
+
+/* Flash */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpiob)
+#define AO_M25_SPI_CS_MASK (1 << 0)
+#define AO_M25_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+
+#define HAS_SERIAL_1 1
+#define SERIAL_1_PB6_PB7 1
+#define USE_SERIAL_1_STDIN 0
+
+#define ao_gps_getchar ao_serial1_getchar
+#define ao_gps_putchar ao_serial1_putchar
+#define ao_gps_set_speed ao_serial1_set_speed
+#define ao_gps_fifo (ao_usart_rx_fifo)
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_RDF 1
+#define HAS_APRS 1
+#define HAS_RADIO_RECV 0
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 0
+#define HAS_LOG 1
+#define FLIGHT_LOG_APPEND 1
+#define HAS_TRACKER 1
+#define LOG_ADC 0
+
+#define AO_CONFIG_DEFAULT_APRS_INTERVAL 0
+#define AO_CONFIG_DEFAULT_RADIO_POWER 0xc0
+
+/*
+ * GPS
+ */
+
+#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_9600
+
+
+/*
+ * Radio (cc1120)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpioa)
+#define AO_CC1200_SPI_CS_PIN 5
+#define AO_CC1200_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+#define AO_CC1200_SPI stm_spi1
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
+
+#define AO_CC1200_INT_PORT (&stm_gpioa)
+#define AO_CC1200_INT_PIN 4
+#define AO_CC1200_MCU_WAKEUP_PORT (&stm_gpioa)
+#define AO_CC1200_MCU_WAKEUP_PIN (0)
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO 3
+#define AO_CC1200_MARC_GPIO_IOCFG CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO 0
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telegps-v2.0/ao_telegps.c b/src/telegps-v2.0/ao_telegps.c
new file mode 100644
index 00000000..7a923d11
--- /dev/null
+++ b/src/telegps-v2.0/ao_telegps.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_tracker.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_exti_init();
+
+ ao_storage_init();
+
+ ao_serial_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+ ao_radio_init();
+
+#if HAS_ADC
+ ao_adc_init();
+#endif
+
+ ao_gps_init();
+#if HAS_LOG
+ ao_log_init();
+#endif
+
+ ao_tracker_init();
+
+ ao_telemetry_init();
+
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+ ao_config_init();
+
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/telegps-v2.0/flash-loader/Makefile b/src/telegps-v2.0/flash-loader/Makefile
new file mode 100644
index 00000000..c0659698
--- /dev/null
+++ b/src/telegps-v2.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telegps-v2.0
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/telegps-v2.0/flash-loader/ao_pins.h b/src/telegps-v2.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..a83dbaa2
--- /dev/null
+++ b/src/telegps-v2.0/flash-loader/ao_pins.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2017 Bdale Garbee <bdale@gag.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin 5 on debug connector */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 15
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telelco-v0.3/ao_pins.h b/src/telelco-v0.3/ao_pins.h
index d874a19b..dd4aaafb 100644
--- a/src/telelco-v0.3/ao_pins.h
+++ b/src/telelco-v0.3/ao_pins.h
@@ -89,6 +89,7 @@
#define AO_CC1200_SPI_CS_PIN 0
#define AO_CC1200_SPI_BUS AO_SPI_2_PD1_PD3_PD4
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpioc)
#define AO_CC1200_INT_PIN (15)
diff --git a/src/telelcotwo-v0.1/ao_pins.h b/src/telelcotwo-v0.1/ao_pins.h
index 714a5c3a..60e94c67 100644
--- a/src/telelcotwo-v0.1/ao_pins.h
+++ b/src/telelcotwo-v0.1/ao_pins.h
@@ -91,6 +91,7 @@
#define AO_CC1200_SPI_CS_PIN 7
#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpiob)
#define AO_CC1200_INT_PIN (11)
diff --git a/src/telemega-v2.0/ao_pins.h b/src/telemega-v2.0/ao_pins.h
index b1c472da..c7c8ad19 100644
--- a/src/telemega-v2.0/ao_pins.h
+++ b/src/telemega-v2.0/ao_pins.h
@@ -309,6 +309,7 @@ struct ao_adc {
#define AO_CC1200_SPI_CS_PIN 5
#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpioe)
#define AO_CC1200_INT_PIN 1
diff --git a/src/telemetrum-v3.0/ao_pins.h b/src/telemetrum-v3.0/ao_pins.h
index ccf2f18f..b937b422 100644
--- a/src/telemetrum-v3.0/ao_pins.h
+++ b/src/telemetrum-v3.0/ao_pins.h
@@ -259,6 +259,7 @@ struct ao_adc {
#define AO_CC1200_SPI_CS_PIN 2
#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1200_SPI stm_spi2
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_FAST
#define AO_CC1200_INT_PORT (&stm_gpioa)
#define AO_CC1200_INT_PIN (3)
diff --git a/src/telemini-v2.0/ao_pins.h b/src/telemini-v2.0/ao_pins.h
index 4f1d36df..d2aa4c2d 100644
--- a/src/telemini-v2.0/ao_pins.h
+++ b/src/telemini-v2.0/ao_pins.h
@@ -115,8 +115,8 @@
#define AO_IGNITER_FIRE_TIME AO_MS_TO_TICKS(50)
#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
-#define AO_SEND_MINI
-#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMINI
+#define AO_SEND_MINI AO_TELEMETRY_MINI2
+#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMINI2
/*
* ADC
diff --git a/src/telemini-v3.0/Makefile b/src/telemini-v3.0/Makefile
new file mode 100644
index 00000000..4713b3ad
--- /dev/null
+++ b/src/telemini-v3.0/Makefile
@@ -0,0 +1,93 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_cc1200.h \
+ ao_cc1200_CC1200.h \
+ stm32f0.h
+
+#
+# Common AltOS sources
+#
+
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_boot_chain.c \
+ ao_romconfig.c \
+ ao_product.c \
+ ao_mutex.c \
+ ao_panic.c \
+ ao_stdio.c \
+ ao_storage.c \
+ ao_report.c \
+ ao_ignite.c \
+ ao_flight.c \
+ ao_kalman.c \
+ ao_sample.c \
+ ao_data.c \
+ ao_convert_pa.c \
+ ao_convert_volt.c \
+ ao_task.c \
+ ao_log.c \
+ ao_log_mini.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_timer.c \
+ ao_exti_stm.c \
+ ao_spi_stm.c \
+ ao_adc_stm.c \
+ ao_usb_stm.c \
+ ao_m25.c \
+ ao_ms5607.c \
+ ao_cc1200.c \
+ ao_telemetry.c \
+ ao_packet_slave.c \
+ ao_beep_stm.c \
+ ao_packet.c
+
+PRODUCT=TeleMini-v3.0
+PRODUCT_DEF=-DTELEMINI_V_3_0
+IDPRODUCT=0x0027
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
+
+PROGNAME=telemini-v3.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telemini.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ)
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+load: $(PROG)
+ lpc-load $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telemini-v3.0/ao_pins.h b/src/telemini-v3.0/ao_pins.h
new file mode 100644
index 00000000..351d28d8
--- /dev/null
+++ b/src/telemini-v3.0/ao_pins.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define HAS_BEEP 1
+#define HAS_SERIAL_1 0
+#define HAS_BATTERY_REPORT 1
+
+#define AO_STACK_SIZE 448
+
+#define IS_FLASH_LOADER 0
+
+/* 48MHz clock based on 16MHz reference */
+//#define AO_HSI48 1
+#define AO_HSE 16000000
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_3
+#define AO_RCC_CFGR2_PLLDIV STM_RCC_CFGR2_PREDIV_1
+#define AO_PLLMUL 3
+#define AO_PLLDIV 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 40MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+#define AO_USB_FORCE_IDLE 1
+
+#define PACKET_HAS_SLAVE 1
+
+#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMINI3
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX ((uint32_t) 112 * (uint32_t) 1024)
+
+#define HAS_BOOT_RADIO 0
+
+#define HAS_ACCEL 0
+#define HAS_GPS 0
+#define HAS_RADIO 1
+#define HAS_RADIO_RATE 1
+#define HAS_FLIGHT 1
+#define HAS_EEPROM 1
+#define HAS_TELEMETRY 1
+#define AO_SEND_MINI AO_TELEMETRY_MINI3
+#define HAS_APRS 0
+#define HAS_LOG 1
+#define USE_INTERNAL_FLASH 0
+#define HAS_IGNITE 1
+#define HAS_IGNITE_REPORT 1
+#define AO_SMALL_ALTITUDE_TABLE 1
+
+/* Beeper is on Tim1 CH3 */
+#define BEEPER_CHANNEL 4
+#define BEEPER_TIMER 2
+#define BEEPER_PORT (&stm_gpioa)
+#define BEEPER_PIN 3
+
+/* SPI */
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_PB3_PB4_PB5 1
+#define SPI_1_OSPEEDR STM_OSPEEDR_MEDIUM
+
+/* M25 */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpioa)
+#define AO_M25_SPI_CS_MASK (1 << 4)
+#define AO_M25_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+
+/* MS5607 */
+
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT (&stm_gpioa)
+#define AO_MS5607_CS_PIN 15
+#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS_PIN)
+#define AO_MS5607_MISO_PORT (&stm_gpiob)
+#define AO_MS5607_MISO_PIN 4
+#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO_PIN)
+#define AO_MS5607_SPI_INDEX AO_SPI_1_PB3_PB4_PB5
+#define AO_MS5607_SPI_SPEED AO_SPI_SPEED_12MHz
+
+/* CC1200 */
+
+// #define AO_RADIO_CAL_DEFAULT 5695733
+#define AO_RADIO_CAL_DEFAULT 5695717
+
+#define AO_FEC_DEBUG 0
+#define CC1200_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpiob)
+#define AO_CC1200_SPI_CS_PIN 0
+#define AO_CC1200_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+#define AO_CC1200_SPI stm_spi1
+#define AO_CC1200_SPI_SPEED AO_SPI_SPEED_12MHz
+
+#define AO_CC1200_INT_PORT (&stm_gpiob)
+#define AO_CC1200_INT_PIN 1
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+
+#define AO_DATA_RING 16
+
+/*
+ * ADC
+ */
+
+#define HAS_ADC 1
+
+#define AO_ADC_PIN0_PORT (&stm_gpioa) /* sense_m */
+#define AO_ADC_PIN0_PIN 0
+#define AO_ADC_PIN0_CH 0
+#define AO_ADC_PIN1_PORT (&stm_gpioa) /* sense_a */
+#define AO_ADC_PIN1_PIN 1
+#define AO_ADC_PIN1_CH 1
+#define AO_ADC_PIN2_PORT (&stm_gpioa) /* v_batt */
+#define AO_ADC_PIN2_PIN 2
+#define AO_ADC_PIN2_CH 2
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_IOPAEN))
+
+#define AO_NUM_ADC 3
+
+struct ao_adc {
+ int16_t sense_m;
+ int16_t sense_a;
+ int16_t v_batt;
+};
+
+/*
+ * Igniter
+ */
+
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+#define AO_IGNITER_DROGUE_PORT (&stm_gpiob)
+#define AO_IGNITER_DROGUE_PIN 7
+#define AO_IGNITER_SET_DROGUE(v) ao_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, AO_IGNITER_DROGUE, v)
+
+#define AO_IGNITER_MAIN_PORT (&stm_gpiob)
+#define AO_IGNITER_MAIN_PIN 6
+#define AO_IGNITER_SET_MAIN(v) ao_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, AO_IGNITER_MAIN, v)
+
+#define AO_SENSE_DROGUE(p) ((p)->adc.sense_a)
+#define AO_SENSE_MAIN(p) ((p)->adc.sense_m)
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5u apogee: %5d main: %5d batt: %5d\n", \
+ (p)->tick, (p)->adc.sense_a, (p)->adc.sense_m, (p)->adc.v_batt)
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 56 /* 5.6k */
+#define AO_BATTERY_DIV_MINUS 100 /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS 100 /* 100k */
+#define AO_IGNITE_DIV_MINUS 27 /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
diff --git a/src/telemini-v3.0/ao_telemini.c b/src/telemini-v3.0/ao_telemini.c
new file mode 100644
index 00000000..82c1acd4
--- /dev/null
+++ b/src/telemini-v3.0/ao_telemini.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+void
+main(void)
+{
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+
+ ao_dma_init();
+ ao_spi_init();
+ ao_exti_init();
+
+ ao_adc_init();
+
+#if HAS_BEEP
+ ao_beep_init();
+#endif
+#if HAS_SERIAL_1
+ ao_serial_init();
+#endif
+#if HAS_USB
+ ao_usb_init();
+#endif
+ ao_cmd_init();
+
+ ao_ms5607_init();
+
+ ao_storage_init();
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+ ao_telemetry_init();
+ ao_radio_init();
+ ao_packet_slave_init(TRUE);
+ ao_igniter_init();
+ ao_config_init();
+
+ ao_start_scheduler();
+}
diff --git a/src/telemini-v3.0/flash-loader/Makefile b/src/telemini-v3.0/flash-loader/Makefile
new file mode 100644
index 00000000..8b628552
--- /dev/null
+++ b/src/telemini-v3.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemini-v3.0
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/telemini-v3.0/flash-loader/ao_pins.h b/src/telemini-v3.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..fea9a645
--- /dev/null
+++ b/src/telemini-v3.0/flash-loader/ao_pins.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* beeper to 3.3V for boot loader mode */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 3
+#define AO_BOOT_APPLICATION_VALUE 0
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_DOWN
+
+/* USB */
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/test/Makefile b/src/test/Makefile
index 02e1d22b..a22abe46 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -1,16 +1,16 @@
-vpath % ..:../kernel:../drivers:../util:../micropeak:../aes:../product
+vpath % ..:../kernel:../drivers:../util:../micropeak:../aes:../product:../lisp
PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \
ao_flight_test_metrum \
ao_gps_test ao_gps_test_skytraq ao_gps_test_ublox ao_convert_test ao_convert_pa_test ao_fec_test \
ao_aprs_test ao_micropeak_test ao_fat_test ao_aes_test ao_int64_test \
- ao_ms5607_convert_test ao_quaternion_test
+ ao_ms5607_convert_test ao_quaternion_test ao_lisp_test
INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h ao_quaternion.h
KALMAN=make-kalman
-CFLAGS=-I.. -I. -I../kernel -I../drivers -I../micropeak -I../product -O0 -g -Wall
+CFLAGS=-I.. -I. -I../kernel -I../drivers -I../micropeak -I../product -I../lisp -O0 -g -Wall -DAO_LISP_TEST -no-pie
all: $(PROGS) ao_aprs_data.wav
@@ -88,3 +88,12 @@ ao_ms5607_convert_test: ao_ms5607_convert_test.c ao_ms5607_convert_8051.c ao_int
ao_quaternion_test: ao_quaternion_test.c ao_quaternion.h
cc $(CFLAGS) -o $@ ao_quaternion_test.c -lm
+AO_LISP_OBJS = ao_lisp_test.o ao_lisp_mem.o ao_lisp_cons.o ao_lisp_string.o \
+ ao_lisp_atom.o ao_lisp_int.o ao_lisp_eval.o ao_lisp_poly.o \
+ ao_lisp_builtin.o ao_lisp_read.o ao_lisp_rep.o ao_lisp_frame.o \
+ ao_lisp_lambda.o ao_lisp_error.o ao_lisp_save.o ao_lisp_stack.o
+
+ao_lisp_test: $(AO_LISP_OBJS)
+ cc $(CFLAGS) -o $@ $(AO_LISP_OBJS)
+
+$(AO_LISP_OBJS): ao_lisp.h ao_lisp_const.h ao_lisp_os.h
diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c
index 3852668a..941bf954 100644
--- a/src/test/ao_aprs_test.c
+++ b/src/test/ao_aprs_test.c
@@ -60,6 +60,20 @@ ao_aprs_bit(uint8_t bit)
void
ao_radio_send_aprs(ao_radio_fill_func fill);
+static void
+aprs_bit_debug(uint8_t tx_bit)
+{
+ fprintf (stderr, "bit %d\n", tx_bit);
+}
+
+static void
+aprs_byte_debug(uint8_t tx_byte)
+{
+ fprintf(stderr, "byte %02x\n", tx_byte);
+}
+#define APRS_BIT_DEBUG(x) aprs_bit_debug(x)
+#define APRS_BYTE_DEBUG(y) aprs_byte_debug(y)
+
#include <ao_aprs.c>
/*
@@ -103,7 +117,7 @@ audio_gap(int secs)
// This is where we go after reset.
int main(int argc, char **argv)
{
- audio_gap(1);
+// audio_gap(1);
ao_gps_data.latitude = (45.0 + 28.25 / 60.0) * 10000000;
ao_gps_data.longitude = (-(122 + 44.2649 / 60.0)) * 10000000;
diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c
index bd7f2ff8..25ddb48f 100644
--- a/src/test/ao_flight_test.c
+++ b/src/test/ao_flight_test.c
@@ -58,6 +58,7 @@ int ao_gps_new;
#define HAS_HMC5883 1
#define HAS_BEEP 1
#define AO_CONFIG_MAX_SIZE 1024
+#define AO_MMA655X_INVERT 0
struct ao_adc {
int16_t sense[AO_ADC_NUM_SENSE];
@@ -71,6 +72,7 @@ struct ao_adc {
#define AO_ADC_NUM_SENSE 2
#define HAS_MS5607 1
#define HAS_MMA655X 1
+#define AO_MMA655X_INVERT 1
#define HAS_BEEP 1
#define AO_CONFIG_MAX_SIZE 1024
@@ -373,6 +375,8 @@ uint16_t prev_tick;
#define AO_PYRO_2 2
#define AO_PYRO_3 3
+#define PYRO_DBG 1
+
static void
ao_pyro_pin_set(uint8_t pin, uint8_t value)
{
diff --git a/src/test/ao_lisp_os.h b/src/test/ao_lisp_os.h
new file mode 100644
index 00000000..9ff2e1fe
--- /dev/null
+++ b/src/test/ao_lisp_os.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LISP_OS_H_
+#define _AO_LISP_OS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define AO_LISP_POOL_TOTAL 3072
+#define AO_LISP_SAVE 1
+#define DBG_MEM_STATS 1
+
+extern int ao_lisp_getc(void);
+
+static inline void
+ao_lisp_os_flush() {
+ fflush(stdout);
+}
+
+static inline void
+ao_lisp_abort(void)
+{
+ abort();
+}
+
+static inline void
+ao_lisp_os_led(int led)
+{
+ printf("leds set to 0x%x\n", led);
+}
+
+static inline void
+ao_lisp_os_delay(int delay)
+{
+ if (!delay)
+ return;
+ struct timespec ts = {
+ .tv_sec = delay / 1000,
+ .tv_nsec = (delay % 1000) * 1000000,
+ };
+ nanosleep(&ts, NULL);
+}
+#endif
diff --git a/src/test/ao_lisp_test.c b/src/test/ao_lisp_test.c
new file mode 100644
index 00000000..68e3a202
--- /dev/null
+++ b/src/test/ao_lisp_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao_lisp.h"
+#include <stdio.h>
+
+static FILE *ao_lisp_file;
+static int newline = 1;
+
+static char save_file[] = "lisp.image";
+
+int
+ao_lisp_os_save(void)
+{
+ FILE *save = fopen(save_file, "w");
+
+ if (!save) {
+ perror(save_file);
+ return 0;
+ }
+ fwrite(ao_lisp_pool, 1, AO_LISP_POOL_TOTAL, save);
+ fclose(save);
+ return 1;
+}
+
+int
+ao_lisp_os_restore_save(struct ao_lisp_os_save *save, int offset)
+{
+ FILE *restore = fopen(save_file, "r");
+ size_t ret;
+
+ if (!restore) {
+ perror(save_file);
+ return 0;
+ }
+ fseek(restore, offset, SEEK_SET);
+ ret = fread(save, sizeof (struct ao_lisp_os_save), 1, restore);
+ fclose(restore);
+ if (ret != 1)
+ return 0;
+ return 1;
+}
+
+int
+ao_lisp_os_restore(void)
+{
+ FILE *restore = fopen(save_file, "r");
+ size_t ret;
+
+ if (!restore) {
+ perror(save_file);
+ return 0;
+ }
+ ret = fread(ao_lisp_pool, 1, AO_LISP_POOL_TOTAL, restore);
+ fclose(restore);
+ if (ret != AO_LISP_POOL_TOTAL)
+ return 0;
+ return 1;
+}
+
+int
+ao_lisp_getc(void)
+{
+ int c;
+
+ if (ao_lisp_file)
+ return getc(ao_lisp_file);
+
+ if (newline) {
+ printf("> ");
+ newline = 0;
+ }
+ c = getchar();
+ if (c == '\n')
+ newline = 1;
+ return c;
+}
+
+int
+main (int argc, char **argv)
+{
+ while (*++argv) {
+ ao_lisp_file = fopen(*argv, "r");
+ if (!ao_lisp_file) {
+ perror(*argv);
+ exit(1);
+ }
+ ao_lisp_read_eval_print();
+ fclose(ao_lisp_file);
+ ao_lisp_file = NULL;
+ }
+ ao_lisp_read_eval_print();
+
+ printf ("collects: full: %d incremental %d\n",
+ ao_lisp_collects[AO_LISP_COLLECT_FULL],
+ ao_lisp_collects[AO_LISP_COLLECT_INCREMENTAL]);
+
+ printf ("freed: full %d incremental %d\n",
+ ao_lisp_freed[AO_LISP_COLLECT_FULL],
+ ao_lisp_freed[AO_LISP_COLLECT_INCREMENTAL]);
+
+ printf("loops: full %d incremental %d\n",
+ ao_lisp_loops[AO_LISP_COLLECT_FULL],
+ ao_lisp_loops[AO_LISP_COLLECT_INCREMENTAL]);
+
+ printf("loops per collect: full %f incremental %f\n",
+ (double) ao_lisp_loops[AO_LISP_COLLECT_FULL] /
+ (double) ao_lisp_collects[AO_LISP_COLLECT_FULL],
+ (double) ao_lisp_loops[AO_LISP_COLLECT_INCREMENTAL] /
+ (double) ao_lisp_collects[AO_LISP_COLLECT_INCREMENTAL]);
+
+ printf("freed per collect: full %f incremental %f\n",
+ (double) ao_lisp_freed[AO_LISP_COLLECT_FULL] /
+ (double) ao_lisp_collects[AO_LISP_COLLECT_FULL],
+ (double) ao_lisp_freed[AO_LISP_COLLECT_INCREMENTAL] /
+ (double) ao_lisp_collects[AO_LISP_COLLECT_INCREMENTAL]);
+
+ printf("freed per loop: full %f incremental %f\n",
+ (double) ao_lisp_freed[AO_LISP_COLLECT_FULL] /
+ (double) ao_lisp_loops[AO_LISP_COLLECT_FULL],
+ (double) ao_lisp_freed[AO_LISP_COLLECT_INCREMENTAL] /
+ (double) ao_lisp_loops[AO_LISP_COLLECT_INCREMENTAL]);
+}
diff --git a/src/test/hanoi.lisp b/src/test/hanoi.lisp
new file mode 100644
index 00000000..e2eb0fa0
--- /dev/null
+++ b/src/test/hanoi.lisp
@@ -0,0 +1,155 @@
+;
+; Towers of Hanoi
+;
+; Copyright © 2016 Keith Packard <keithp@keithp.com>
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+
+ ; ANSI control sequences
+
+(defun move-to (col row)
+ (patom "\033[" row ";" col "H")
+ )
+
+(defun clear ()
+ (patom "\033[2J")
+ )
+
+(defun display-string (x y str)
+ (move-to x y)
+ (patom str)
+ )
+
+ ; Here's the pieces to display
+
+(setq stack '(" * " " *** " " ***** " " ******* " " ********* " "***********"))
+
+ ; Here's all of the stacks of pieces
+ ; This is generated when the program is run
+
+(setq stacks nil)
+
+ ; Display one stack, clearing any
+ ; space above it
+
+(defun display-stack (x y clear stack)
+ (cond ((= 0 clear)
+ (cond (stack
+ (display-string x y (car stack))
+ (display-stack x (1+ y) 0 (cdr stack))
+ )
+ )
+ )
+ (t
+ (display-string x y " ")
+ (display-stack x (1+ y) (1- clear) stack)
+ )
+ )
+ )
+
+ ; Position of the top of the stack on the screen
+ ; Shorter stacks start further down the screen
+
+(defun stack-pos (y stack)
+ (- y (length stack))
+ )
+
+ ; Display all of the stacks, spaced 20 columns apart
+
+(defun display-stacks (x y stacks)
+ (cond (stacks
+ (display-stack x 0 (stack-pos y (car stacks)) (car stacks))
+ (display-stacks (+ x 20) y (cdr stacks)))
+ )
+ )
+
+ ; Display all of the stacks, then move the cursor
+ ; out of the way and flush the output
+
+(defun display ()
+ (display-stacks 0 top stacks)
+ (move-to 1 21)
+ (flush)
+ )
+
+ ; Reset stacks to the starting state, with
+ ; all of the pieces in the first stack and the
+ ; other two empty
+
+(defun reset-stacks ()
+ (setq stacks (list stack nil nil))
+ (setq top (+ (length stack) 3))
+ (length stack)
+ )
+
+ ; more functions which could usefully
+ ; be in the rom image
+
+(defun min (a b)
+ (cond ((< a b) a)
+ (b)
+ )
+ )
+
+ ; Replace a stack in the list of stacks
+ ; with a new value
+
+(defun replace (list pos member)
+ (cond ((= pos 0) (cons member (cdr list)))
+ ((cons (car list) (replace (cdr list) (1- pos) member)))
+ )
+ )
+
+ ; Move a piece from the top of one stack
+ ; to the top of another
+
+(setq move-delay 100)
+
+(defun move-piece (from to)
+ (let ((from-stack (nth stacks from))
+ (to-stack (nth stacks to))
+ (piece (car from-stack)))
+ (setq from-stack (cdr from-stack))
+ (setq to-stack (cons piece to-stack))
+ (setq stacks (replace stacks from from-stack))
+ (setq stacks (replace stacks to to-stack))
+ (display)
+ (delay move-delay)
+ )
+ )
+
+; The implementation of the game
+
+(defun _hanoi (n from to use)
+ (cond ((= 1 n)
+ (move-piece from to)
+ )
+ (t
+ (_hanoi (1- n) from use to)
+ (_hanoi 1 from to use)
+ (_hanoi (1- n) use to from)
+ )
+ )
+ )
+
+ ; A pretty interface which
+ ; resets the state of the game,
+ ; clears the screen and runs
+ ; the program
+
+(defun hanoi ()
+ (setq len (reset-stacks))
+ (clear)
+ (_hanoi len 0 1 2)
+ (move-to 0 23)
+ t
+ )