]> Chaos Git - corbenik/bdfe.git/commitdiff
Initial commit
authorAndrey Chilikin <achilikin@gmail.com>
Sun, 23 Nov 2014 18:29:20 +0000 (18:29 +0000)
committerAndrey Chilikin <achilikin@gmail.com>
Sun, 23 Nov 2014 18:29:20 +0000 (18:29 +0000)
14 files changed:
LICENSE
Makefile [new file with mode: 0644]
README.md
bdf.c [new file with mode: 0644]
bdf.h [new file with mode: 0644]
font816.h [new file with mode: 0644]
font88.h [new file with mode: 0644]
main.c [new file with mode: 0644]
ossd_i2c.c [new file with mode: 0644]
ossd_i2c.h [new file with mode: 0644]
pi2c.c [new file with mode: 0644]
pi2c.h [new file with mode: 0644]
rterm.c [new file with mode: 0644]
rterm.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
index 6f378403b1d42ad00f403eeecaf0cd3e23b919c8..da69a0e23561c341edd224e5d75eb7bfeed8c052 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014, Andrey Chilikin
+Copyright (c) 2014, Andrey Chilikin https://github.com/achilikin
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b40e49d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+CXX = g++
+CFLAGS = -Wall -Werror
+CFLAGS += -g
+#CFLAGS += -O3
+LIBS    =
+
+CORE = bdfe
+OBJS = main.o ossd_i2c.o pi2c.o bdf.o rterm.o
+HFILES = Makefile pi2c.h ossd_i2c.h rterm.h font88.h font816.h
+CFILES = ossd_i2c.c bdf.c rterm.c main.c 
+
+all: $(CORE)
+
+$(CORE): $(OBJS) $(CFILES) $(HFILES)
+       $(CXX) $(CFLAGS) -o $(CORE) $(OBJS) $(LIBS)
+
+clean:
+       rm -f $(CORE)
+       rm -f *.o
+
+%.o: %.c $(HFILES)
+       $(CXX) -c $(CFLAGS) $< -o $@
+
+
index 93f3222866ebee832d80881f00ed52c2ff344a62..5768f9e51be549ae32693fe7dfeec712fa5143c9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,67 @@
 bdfe
-====
+----
+
+**bdfe** might sound like crazy alphabet mixture but actually it is short name for [Bitmap Distribution Format Exporter](http://en.wikipedia.org/wiki/Glyph_Bitmap_Distribution_Format "bdf wiki") - utility which can extract subsets or export all glyphs from .bdf font files.
+
+Why? I've decided to add a small oled display to my temperature/humidity station based on modded [MMR-70 FM Music Transmitter](https://github.com/achilikin/mmr70mod "MMR70 mod") but could not find any free 8x8 and 8x16 fonts in C format. There are tons of free fonts in bdf format because for years bdf has been used by UNIX X Windows. Converters available as well, but I needed very specific converter which would create fonts for SSD1306 based OLED screens.
+
+So here it is - bdf font converter which can export glyphs from bdf and present them as array of bytes. It also can rotate 8x8 and 8x16 glyphs CCW so they can be used on displays with SSD1306 controller.
+
+I'm running it with my Raspberry Pi (same RPi I use to program MMR70 ATmega) but it should run on any other Linux machine as well.
+
+
+Command line options
+--------------------
+
+bdfe understands a few command line options, in plain English words or, for lazy people like me, in short single letter option which happened to be the first letter of the corresponding word. 
+
+```
+bdfe [options] <bdf file>
+  options are:
+  header:     print file header
+  verbose:    add extra info to the header
+  line:       one line per glyph
+  subset a-b: subset of glyphs to convert a to b, default 32-126
+  all:        print all glyphs, not just 32-126
+  native:     do not adjust font height 8 pixels
+  ascender H: add extra ascender of H pixels per glyph
+  rotate:     rotate glyphs' bitmaps CCW
+  display A:  show converted font on SSD1306 compatible display
+              using I2C bus 1, hexadecimal address A (default 3C)
+  updown:     display orientation is upside down
+```
+
+So ```bdfe header verbose line all native file.bdf``` and ```bdfe -h -v -l -a -n file.bdf``` are the same.
+
+There is no output file option - just redirect bdfe output to a file: ```bdfe -h -v -l -a -n font.bdf > font.h```
+OLED display is not needed for conversion but useful to have as you can immediately see if font is suitable with under-/over-line or inverse attributes. By default I2C slave address x3C is used, on my display this address is selected by connecting DC to GND, connecting it to Vcc switches address to x3D. If you do not know address of your SSD1306 display use ```i2cdetect  
+
+Source code
+-----------
+
+Source code can be split to standalone modules which might be used in other projects separately:
+
+```bdfe.h, bdfe.c``` - BDF file parser, single (quite big) function.
+```rterm.h, rterm.c``` - raw terminal input, kind of old nice getch().
+```ossd_i2c.h, ossd_i2c.c``` - OLED SSD1306 controller interface using I2C bus. Simple text mode only with direct access to SSD1306 registers, no shadow graphics buffer - memory on MMR70 ATmega32 MCU is a precious resource and anyway MMR70 will update screen only once in a while. Currently only fonts 8 and 16 bits high are supported. Underline, overline and inverse attributes can be used. 
+```pi2c.h, pi2c.c``` - basic I2C wrapper for i2c-dev library, allows to write to SSD1306 connected to I2C bus.
+```font8x8.h, font8x16.h``` - converted bdf files.
+```main.c``` - puts all of above together and does the work.
+
+Examples
+--------
+![bdfe screen](http://achilikin.com/github/bdfe-01.png)
+Converting 7x8 font... Use ```bdfe ... | less``` to scroll exported font up and down.
+
+![8x8 screen](http://achilikin.com/github/font8x8.gif) ![8x16 screen](http://achilikin.com/github/font8x16.gif) ![mmr70 screen](http://achilikin.com/github/mmr70.png)
+
+OLED screen with 8x8 and 8x16 fonts converted and screen connected to a modded version of [MMR-70](https://github.com/achilikin/mmr70mod") with current temperature and humidity reading.
+For 8x16 font command option ```ascender 1``` was used to lower glyphs 1 pixel down.
+  
+
+Licence
+-------
+BSD
 
-BDF Exporter - converts fonts from BDF files to C structures
diff --git a/bdf.c b/bdf.c
new file mode 100644 (file)
index 0000000..7a9d800
--- /dev/null
+++ b/bdf.c
@@ -0,0 +1,308 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Basic BDF Exporter - converts BDF files to C structures.
+       Only 8 bits wide fonts are supported.
+*/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bdf.h"
+
+// check if 'buf' starts with 'key' and store pointer to the argument
+static char *key_arg(char *buf, const char *key, char **arg)
+{
+       char *end;
+       size_t len = strlen(key);
+
+       if (strncmp(buf, key, len) == 0) {
+               end = buf + len;
+               if (*end > ' ')
+                       return NULL;
+               if (arg == NULL)
+                       return buf;
+               for(; *end <= ' ' && *end != '\0'; end++);
+               *arg = end;
+               return buf;
+       }
+
+       return NULL;
+}
+
+// rotate glyph bitmap CCW
+static uint32_t rotate_glyph(const uint8_t *glyph, unsigned gw, int gh, uint8_t *grot)
+{
+       uint32_t dy = 0;
+
+       do {
+               for(uint8_t n = 0; n < gw; n++) {
+                       uint8_t out = 0;
+                       uint8_t mask = 0x80 >> n;
+                       for(uint8_t b = 0; b < 8; b++) {
+                               if (glyph[b] & mask)
+                                       out |= 1 << b;
+                       }
+                       grot[dy++] = out;
+               }
+               glyph += 8;
+       } while((gh -= 8) > 0);
+
+       return dy;
+}
+
+bdfe_t *bdf_convert(const char *name, unsigned gmin, unsigned gmax, unsigned ascender, int flags)
+{
+       FILE *fp;
+       char *arg;
+       bdfe_t *font = NULL;
+       char buf[BUFSIZ];
+       char startchar[BUFSIZ];
+
+       if (name == NULL || (fp = fopen(name, "r")) == NULL)
+               return NULL;
+
+       memset(buf, 0, sizeof(buf));
+       int mute = flags & BDF_MUTE;
+       
+       if (!mute && (flags & BDF_HEADER)) {
+               printf("// File generated by 'bdfe");
+               if (flags & BDF_NATIVE)
+                       printf(" -n");
+               if (flags & BDF_ROTATE)
+                       printf(" -r");
+               if (ascender)
+                       printf(" -a %d", ascender);
+               printf(" -s %d-%d %s'\n", gmin, gmax, basename(name));
+       }
+
+       // parse file header up to 'CHARS' keyword
+       unsigned nchars = 0, dx = 0, dy = 0, descent = 0;
+       while(fgets(buf, sizeof(buf) - 2, fp) != NULL) {
+               arg = strchr(buf, '\0');
+               while(*arg < ' ' && arg != buf) *arg-- = '\0';
+
+               if (!mute && (flags & BDF_HEADER)) {
+                       if (key_arg(buf, "FONT", &arg))
+                               printf("// %s\n", buf);
+                       if (key_arg(buf, "COMMENT", &arg))
+                               printf("// %s\n", buf);
+                       if (key_arg(buf, "COPYRIGHT", &arg))
+                               printf("// %s\n", buf);
+                       if (key_arg(buf, "FONT_ASCENT", &arg))
+                               printf("// %s\n", buf);
+               }
+
+               if (key_arg(buf, "FONTBOUNDINGBOX", &arg)) {
+                       if (!mute && (flags & BDF_HEADER))
+                               printf("// %s\n", buf);
+                       dx = strtoul(arg, &arg, 10);
+                       dy = strtoul(arg, &arg, 10);
+                       strtoul(arg, &arg, 10);
+                       descent = -strtoul(arg, &arg, 10);
+               }
+               if (key_arg(buf, "FONT_DESCENT", &arg)) {
+                       if (!mute && (flags & BDF_HEADER))
+                               printf("// %s\n", buf);
+                       descent = strtoul(arg, &arg, 10);
+               }
+
+               if (key_arg(buf, "CHARS", &arg)) {
+                       nchars = atoi(arg);
+                       break;
+               }
+       }
+
+       if (dy == 0) {
+               fclose(fp);
+               return NULL;
+       }
+
+       // recalculate glyphs x size as we cannot use FONTBOUNDINGBOX
+       // as it is for our gmin/gmax range
+       dx = 0;
+       while(fgets(buf, sizeof(buf) - 2, fp) != NULL) {
+               if (key_arg(buf, "STARTCHAR", &arg)) {
+                       unsigned idx = 0, dw;
+
+                       while(fgets(buf, sizeof(buf) - 2, fp) != NULL) {
+                               if (key_arg(buf, "ENCODING", &arg)) {
+                                       idx = atoi(arg);
+                                       if (idx < gmin || idx > gmax)
+                                               break;
+                               }
+                               if (key_arg(buf, "DWIDTH", &arg)) {
+                                       dw = atoi(arg);
+                                       if (dw > dx)
+                                               dx = dw;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       // limit vertical size to 16 pixels or 2 lines on SSD1306
+       if (dy > 16)
+               dy = 16;
+
+       unsigned gh = dy, gw = dx;
+       // round y size to 8 pixels for SSD1306 text mode
+       if ((flags & BDF_ROTATE) || !(flags & BDF_NATIVE)) {
+               dy = (dy+7) & ~0x07;
+               descent += dy - gh;
+               gh = dy;
+       }
+       
+       if (!mute && (flags & BDF_HEADER))
+               printf("// Converted Font Size %dx%d\n\n", dx, dy);
+       if (ascender > dy/2)
+               ascender = dy/2;
+       // rewind the file pointer ans start parsing glyphs
+       fseek(fp, 0, SEEK_SET);
+
+       unsigned gsize = dy * ((dx + 7) / 8);
+       font = (bdfe_t *)malloc(sizeof(bdfe_t) + gsize*nchars);
+       font->chars = 0;
+       // glyphs output buffer
+       uint8_t *gout = font->font;
+       // glyph storage buffer, big enough to allow displacement manipulations
+       uint8_t *gbuf = (uint8_t *)malloc(gsize*2);
+       memset(gbuf, 0, gsize*2);
+
+       while(fgets(buf, sizeof(buf) - 2, fp) != NULL) {
+               if (key_arg(buf, "STARTCHAR", &arg)) {
+                       unsigned displacement = 0;
+                       unsigned bitmap = 0, i = 0, idx = 0;
+                       unsigned bbw = 0;
+                       int bbox = 0, bboy = 0;
+                       uint8_t *gin = gbuf + gsize;
+                       
+                       memset(gin, 0, gsize);
+                       // store a copy in case if verbose output is on
+                       arg = strchr(buf, '\0');
+                       while(*arg < ' ' && arg != buf) *arg-- = '\0';
+                       strcpy(startchar, buf);
+                       
+                       while(fgets(buf, sizeof(buf) - 2, fp) != NULL) {
+                               arg = strchr(buf, '\0');
+                               while(*arg < ' ' && arg != buf) *arg-- = '\0';
+
+                               if (key_arg(buf, "ENCODING", &arg)) {
+                                       idx = atoi(arg);
+                                       if (idx < gmin || idx > gmax)
+                                               break;
+                                       if (!mute && !(flags & BDF_GPL) && (flags & BDF_VERBOSE)) {
+                                               printf("// %s\n", startchar);
+                                               printf("// %s\n", buf);
+                                       }
+                               }
+                               if (key_arg(buf, "DWIDTH", &arg)) {
+                                       gw = atoi(arg);
+                                       if (gw > 8)
+                                               break;
+                               }
+                               if (key_arg(buf, "BBX", &arg)) {
+                                       if (!mute && !(flags & BDF_GPL) && (flags & BDF_VERBOSE))
+                                               printf("// %s\n", buf);
+                                       bbw = strtol(arg, &arg, 10);
+                                       strtol(arg, &arg, 10); // skip bbh, we'll calculate it (i)
+                                       bbox = strtol(arg, &arg, 10);
+                                       bboy = strtol(arg, &arg, 10);
+                               }
+                               if (key_arg(buf, "ENDCHAR", &arg)) {
+                                       font->chars++;
+                                       // check if baseline alignment is needed
+                                       if (bboy > 0)
+                                               i += bboy;
+                                       if ((bboy < 0) && (i < dy)) {
+                                               displacement = dy - descent - i; // move BBX to origin
+                                               displacement -= bboy;
+                                               i += displacement;
+                                       }
+                                       if (i < (dy - descent))
+                                               displacement += dy - descent - i;
+                                       gin -= displacement + ascender;
+                                       if (flags & BDF_ROTATE) {
+                                               gh = rotate_glyph(gin, dx, dy, gout);
+                                               gw = 8;
+                                       }
+                                       else
+                                               memcpy(gout, gin, dy);
+
+                                       if (!mute) {
+                                               if (flags & BDF_GPL) {
+                                                       printf("\t");
+                                                       for(i = 0; i < dy; i++)
+                                                               printf("0x%02X,", gout[i]);
+                                                       printf(" // %5d", idx);
+                                                       if (isprint(idx))
+                                                               printf(" '%c'", idx);
+                                                       printf("\n");
+                                               }
+                                               else {
+                                                       printf("// %5d '%c' |", idx, isprint(idx) ? idx : ' ');
+                                                       for(i = 0; i < gw; i++)
+                                                               printf("%d", i);
+                                                       printf("|\n");
+                                                       for(i = 0; i < gh; i++) {
+                                                               printf(" 0x%02X, //  %2d|", gout[i], i);
+                                                               for(unsigned bit = 0; bit < gw; bit++) {
+                                                                       if (gout[i] & (0x80 >> bit))
+                                                                               printf("#");
+                                                                       else
+                                                                               printf(" ");
+                                                               }
+                                                               printf("|\n");
+                                                       }
+                                                       printf("\n");
+                                               }
+                                       }
+                                       gout += gh;
+                                       break;
+                               }
+                               if (key_arg(buf, "BITMAP", &arg)) {
+                                       bitmap = 1;
+                                       continue;
+                               }
+                               if (bitmap && (i < dy)) {
+                                       gin[i] = (uint8_t)strtoul(buf, NULL, 16);
+                                       if ((bbw < 8) && (bbox > 0) && (bbox < 8))
+                                               gin[i] = gin[i] >> bbox;
+                                       i++;
+                               }
+                       }
+               }
+       }
+       font->gw  = dx;
+       font->bpg = gh;
+
+       free(gbuf);
+       fclose(fp);
+       return font;
+}
diff --git a/bdf.h b/bdf.h
new file mode 100644 (file)
index 0000000..1a45daa
--- /dev/null
+++ b/bdf.h
@@ -0,0 +1,66 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef __BDF_EXPORTER_H__
+#define __BDF_EXPORTER_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} // dummy bracket for Visual Assist
+#endif
+#endif
+
+#define BDF_NATIVE   0x01 /*< use native height, do not adjust for SSD1306 */
+#define BDF_ROTATE   0x02 /*< rotate glyphs CCW  */
+#define BDF_MUTE     0x04 /*< do not print processed glyphs or header */
+#define BDF_HEADER   0x08 /*< output file header */
+#define BDF_VERBOSE  0x10 /*< verbose glyphs/header output */
+#define BDF_GPL      0x20 /*< one line per glyph */
+
+
+typedef struct bdfe_s
+{
+       unsigned gw;     /*< glyph width      */
+       unsigned bpg;    /*< bytes per glyph  */
+       unsigned chars;  /*< number of glyphs */
+       uint8_t  font[]; /*< glyphs' bitmaps  */
+} bdfe_t;
+
+/**
+ name      - bdf font name
+ gmin/gmax - range of glyphs to be processed
+ flags     - BDF_* above
+ ascender  - extra ascender to be added, in pixels
+ */
+bdfe_t *bdf_convert(const char *name, unsigned gmin, unsigned gmax, unsigned ascender, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/font816.h b/font816.h
new file mode 100644 (file)
index 0000000..009a3b8
--- /dev/null
+++ b/font816.h
@@ -0,0 +1,111 @@
+// File generated by 'bdfe -n -r -a 1 -s 32-126 zevv-peep-iso8859-1-08x16.bdf'
+// FONT -zevv-peep-Medium-R-Normal--16-140-75-75-C-80-ISO8859-1
+// COMMENT -
+// COMMENT http://zevv.nl/play/code/zevv-peep/
+// COMMENT Clean, avoid clutter
+// COMMENT Very clear distinction between similar characters like 0/O/o, i/I/l/1/|
+// COMMENT Suitable for light text on a dark background.
+// COMMENT Not to densly packed to allow a good seperation between characters 
+// COMMENT The zevv-peep font is available under the terms of the MIT license. 
+// COMMENT -
+// FONTBOUNDINGBOX 8 16 0 -3
+// COPYRIGHT "Zevv"
+// FONT_ASCENT 13
+// FONT_DESCENT 3
+// Converted Font Size 8x16
+
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    32 ' '
+       0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x00, //    33 '!'
+       0x00,0x00,0x78,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    34 '"'
+       0x00,0x40,0xF0,0x40,0x40,0xF0,0x40,0x00,0x00,0x02,0x0F,0x02,0x02,0x0F,0x02,0x00, //    35 '#'
+       0x00,0xC0,0x20,0xF0,0x20,0x20,0x00,0x00,0x00,0x08,0x09,0x1F,0x09,0x06,0x00,0x00, //    36 '$'
+       0x00,0x30,0x48,0x30,0xC0,0x20,0x18,0x00,0x00,0x18,0x04,0x03,0x0C,0x12,0x0C,0x00, //    37 '%'
+       0x00,0x60,0x90,0x90,0x60,0x00,0x00,0x00,0x00,0x0E,0x11,0x11,0x12,0x0C,0x12,0x00, //    38 '&'
+       0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    39 '''
+       0x00,0x00,0x00,0xC0,0x30,0x08,0x04,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00, //    40 '('
+       0x00,0x04,0x08,0x30,0xC0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00, //    41 ')'
+       0x00,0x00,0x40,0x80,0x80,0x40,0x00,0x00,0x00,0x01,0x05,0x03,0x03,0x05,0x01,0x00, //    42 '*'
+       0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x0F,0x01,0x01,0x01, //    43 '+'
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x30,0x18,0x08,0x00,0x00,0x00, //    44 ','
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00, //    45 '-'
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x10,0x00,0x00,0x00, //    46 '.'
+       0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00, //    47 '/'
+       0x00,0xE0,0x10,0x08,0x88,0x10,0xE0,0x00,0x00,0x07,0x08,0x11,0x10,0x08,0x07,0x00, //    48 '0'
+       0x00,0x40,0x20,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, //    49 '1'
+       0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x14,0x12,0x11,0x10,0x10,0x00, //    50 '2'
+       0x00,0x10,0x08,0x88,0x88,0x88,0x70,0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x0F,0x00, //    51 '3'
+       0x00,0x00,0xC0,0x30,0x08,0xF8,0x00,0x00,0x00,0x03,0x02,0x02,0x02,0x1F,0x02,0x00, //    52 '4'
+       0x00,0xF8,0x48,0x48,0x48,0x48,0x88,0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x0F,0x00, //    53 '5'
+       0x00,0xF0,0x08,0x88,0x88,0x88,0x10,0x00,0x00,0x0F,0x11,0x10,0x10,0x10,0x0F,0x00, //    54 '6'
+       0x00,0x08,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x18,0x06,0x01,0x00,0x00,0x00, //    55 '7'
+       0x00,0x70,0x88,0x88,0x88,0x88,0x70,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x0F,0x00, //    56 '8'
+       0x00,0xF0,0x08,0x08,0x08,0x88,0xF0,0x00,0x00,0x08,0x11,0x11,0x11,0x10,0x0F,0x00, //    57 '9'
+       0x00,0x00,0x40,0xE0,0x40,0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x10,0x00,0x00,0x00, //    58 ':'
+       0x00,0x00,0x40,0xE0,0x40,0x00,0x00,0x00,0x00,0x40,0x30,0x18,0x08,0x00,0x00,0x00, //    59 ';'
+       0x00,0x00,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x00,0x00, //    60 '<'
+       0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x00, //    61 '='
+       0x00,0x00,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x00,0x10,0x08,0x04,0x02,0x01,0x00, //    62 '>'
+       0x00,0x08,0x08,0x88,0x48,0x30,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x00,0x00, //    63 '?'
+       0x00,0xE0,0x10,0x88,0x48,0x48,0xF0,0x00,0x00,0x07,0x08,0x13,0x14,0x14,0x07,0x00, //    64 '@'
+       0x00,0xF0,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x1F,0x01,0x01,0x01,0x01,0x1F,0x00, //    65 'A'
+       0x00,0xF8,0x88,0x88,0x88,0xF0,0x00,0x00,0x00,0x1F,0x10,0x10,0x10,0x10,0x0F,0x00, //    66 'B'
+       0x00,0xF0,0x08,0x08,0x08,0x08,0x30,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x0C,0x00, //    67 'C'
+       0x00,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x1F,0x10,0x10,0x10,0x08,0x07,0x00, //    68 'D'
+       0x00,0xF8,0x88,0x88,0x88,0x88,0x08,0x00,0x00,0x1F,0x10,0x10,0x10,0x10,0x10,0x00, //    69 'E'
+       0x00,0xF8,0x88,0x88,0x88,0x88,0x08,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00, //    70 'F'
+       0x00,0xF0,0x08,0x08,0x08,0x08,0x30,0x00,0x00,0x0F,0x10,0x10,0x11,0x11,0x0F,0x00, //    71 'G'
+       0x00,0xF8,0x80,0x80,0x80,0x80,0xF8,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x1F,0x00, //    72 'H'
+       0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x10,0x10,0x1F,0x10,0x10,0x00,0x00, //    73 'I'
+       0x00,0x00,0x00,0x08,0x08,0x08,0xF8,0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x0F,0x00, //    74 'J'
+       0x00,0xF8,0x80,0x40,0xA0,0x10,0x08,0x00,0x00,0x1F,0x00,0x00,0x01,0x06,0x18,0x00, //    75 'K'
+       0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x10,0x10,0x10,0x10,0x10,0x00, //    76 'L'
+       0x00,0xF8,0x30,0xC0,0x00,0xC0,0x30,0xF8,0x00,0x1F,0x00,0x00,0x03,0x00,0x00,0x1F, //    77 'M'
+       0x00,0xF8,0x30,0xC0,0x00,0x00,0xF8,0x00,0x00,0x1F,0x00,0x00,0x03,0x0C,0x1F,0x00, //    78 'N'
+       0x00,0xF0,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x0F,0x00, //    79 'O'
+       0x00,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x1F,0x01,0x01,0x01,0x01,0x00,0x00, //    80 'P'
+       0x00,0xF0,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x0F,0x10,0x10,0x1C,0x30,0x2F,0x00, //    81 'Q'
+       0x00,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x1F,0x01,0x03,0x05,0x09,0x10,0x00, //    82 'R'
+       0x00,0x70,0x88,0x88,0x08,0x08,0x10,0x00,0x00,0x08,0x10,0x10,0x11,0x11,0x0E,0x00, //    83 'S'
+       0x00,0x08,0x08,0x08,0xF8,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, //    84 'T'
+       0x00,0xF8,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x0F,0x00, //    85 'U'
+       0x00,0xF8,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x01,0x06,0x18,0x18,0x06,0x01,0x00, //    86 'V'
+       0x00,0xF8,0x00,0x00,0x80,0x00,0x00,0xF8,0x00,0x07,0x18,0x06,0x01,0x06,0x18,0x07, //    87 'W'
+       0x00,0x18,0x60,0x80,0x80,0x60,0x18,0x00,0x00,0x18,0x06,0x01,0x01,0x06,0x18,0x00, //    88 'X'
+       0x00,0x78,0x80,0x00,0x00,0x80,0x78,0x00,0x00,0x00,0x00,0x01,0x1F,0x00,0x00,0x00, //    89 'Y'
+       0x00,0x08,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x18,0x14,0x13,0x10,0x10,0x10,0x00, //    90 'Z'
+       0x00,0x00,0x00,0xFC,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00, //    91 '['
+       0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x30,0x00, //    92 '\'
+       0x00,0x04,0x04,0x04,0xFC,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00, //    93 ']'
+       0x00,0x20,0x10,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    94 '^'
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00, //    95 '_'
+       0x00,0x00,0x08,0x18,0x30,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    96 '`'
+       0x00,0x00,0x40,0x40,0x40,0x40,0x80,0x00,0x00,0x0E,0x11,0x11,0x11,0x09,0x1F,0x00, //    97 'a'
+       0x00,0xF8,0x80,0x40,0x40,0x40,0x80,0x00,0x00,0x1F,0x08,0x10,0x10,0x10,0x0F,0x00, //    98 'b'
+       0x00,0x80,0x40,0x40,0x40,0x40,0x80,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x08,0x00, //    99 'c'
+       0x00,0x80,0x40,0x40,0x40,0x80,0xF8,0x00,0x00,0x0F,0x10,0x10,0x10,0x08,0x1F,0x00, //   100 'd'
+       0x00,0x80,0x40,0x40,0x40,0x40,0x80,0x00,0x00,0x0F,0x12,0x12,0x12,0x12,0x13,0x00, //   101 'e'
+       0x00,0x00,0xF0,0x08,0x08,0x08,0x10,0x00,0x00,0x01,0x1F,0x01,0x01,0x01,0x00,0x00, //   102 'f'
+       0x00,0x80,0x40,0x40,0x40,0x80,0xC0,0x00,0x00,0x47,0x88,0x88,0x88,0x84,0x7F,0x00, //   103 'g'
+       0x00,0xF8,0x80,0x40,0x40,0x40,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x1F,0x00, //   104 'h'
+       0x00,0x00,0x40,0x40,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, //   105 'i'
+       0x00,0x00,0x00,0x40,0x40,0xD8,0x00,0x00,0x00,0x20,0x40,0x40,0x40,0x3F,0x00,0x00, //   106 'j'
+       0x00,0xF8,0x00,0x00,0x80,0x40,0x00,0x00,0x00,0x1F,0x02,0x03,0x04,0x08,0x10,0x00, //   107 'k'
+       0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x10,0x10,0x10,0x00,0x00, //   108 'l'
+       0x00,0xC0,0x40,0x40,0x80,0x40,0x40,0x80,0x00,0x1F,0x00,0x00,0x07,0x00,0x00,0x1F, //   109 'm'
+       0x00,0xC0,0x80,0x40,0x40,0x40,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x1F,0x00, //   110 'n'
+       0x00,0x80,0x40,0x40,0x40,0x40,0x80,0x00,0x00,0x0F,0x10,0x10,0x10,0x10,0x0F,0x00, //   111 'o'
+       0x00,0xC0,0x80,0x40,0x40,0x40,0x80,0x00,0x00,0xFF,0x04,0x08,0x08,0x08,0x07,0x00, //   112 'p'
+       0x00,0x80,0x40,0x40,0x40,0x80,0xC0,0x00,0x00,0x07,0x08,0x08,0x08,0x04,0xFF,0x00, //   113 'q'
+       0x00,0xC0,0x80,0x40,0x40,0x40,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00, //   114 'r'
+       0x00,0x80,0x40,0x40,0x40,0x40,0x80,0x00,0x00,0x09,0x12,0x12,0x12,0x12,0x0C,0x00, //   115 's'
+       0x00,0x40,0xF0,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x0F,0x10,0x10,0x10,0x08,0x00, //   116 't'
+       0x00,0xC0,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x0F,0x10,0x10,0x10,0x08,0x1F,0x00, //   117 'u'
+       0x00,0xC0,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x01,0x06,0x18,0x18,0x06,0x01,0x00, //   118 'v'
+       0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x0F,0x10,0x08,0x07,0x08,0x10,0x0F, //   119 'w'
+       0x00,0x40,0x80,0x00,0x00,0x80,0x40,0x00,0x00,0x10,0x08,0x05,0x05,0x08,0x10,0x00, //   120 'x'
+       0x00,0xC0,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x47,0x88,0x88,0x88,0x84,0x7F,0x00, //   121 'y'
+       0x00,0x40,0x40,0x40,0x40,0x40,0xC0,0x00,0x00,0x10,0x18,0x14,0x12,0x11,0x10,0x00, //   122 'z'
+       0x00,0x00,0x00,0x00,0xF8,0x04,0x04,0x00,0x00,0x00,0x01,0x01,0x3E,0x40,0x40,0x00, //   123 '{'
+       0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00, //   124 '|'
+       0x00,0x04,0x04,0xF8,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x3E,0x01,0x01,0x00,0x00, //   125 '}'
+       0x00,0x60,0x10,0x10,0x20,0x40,0x40,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //   126 '~'
diff --git a/font88.h b/font88.h
new file mode 100644 (file)
index 0000000..6b10b59
--- /dev/null
+++ b/font88.h
@@ -0,0 +1,121 @@
+// File generated by 'bdfe -r -s 32-126 clR8x8.bdf'
+// COMMENT http://cgit.freedesktop.org/xorg/font/schumacher-misc/tree/clR8x8.bdf
+// COMMENT $Xorg: clR8x8.bdf,v 1.3 2000/08/18 15:17:40 xorgcvs Exp $
+// COMMENT  
+// COMMENT  Copyright 1989 Dale Schumacher, dal@syntel.mn.org
+// COMMENT                 399 Beacon Ave.
+// COMMENT                 St. Paul, MN  55104-3527
+// COMMENT  
+// COMMENT  Permission to use, copy, modify, and distribute this software and
+// COMMENT  its documentation for any purpose and without fee is hereby
+// COMMENT  granted, provided that the above copyright notice appear in all
+// COMMENT  copies and that both that copyright notice and this permission
+// COMMENT  notice appear in supporting documentation, and that the name of
+// COMMENT  Dale Schumacher not be used in advertising or publicity pertaining to
+// COMMENT  distribution of the software without specific, written prior
+// COMMENT  permission.  Dale Schumacher makes no representations about the
+// COMMENT  suitability of this software for any purpose.  It is provided "as
+// COMMENT  is" without express or implied warranty.
+// COMMENT  
+// FONT -Schumacher-Clean-Medium-R-Normal--8-80-75-75-C-80-ISO646.1991-IRV
+// FONTBOUNDINGBOX 8 8 0 -1
+// FONT_ASCENT 7
+// FONT_DESCENT 1
+// COPYRIGHT "Copyright 1989 Dale Schumacher."
+// Converted Font Size 8x8
+
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //    32 ' '
+       0x00,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, //    33 '!'
+       0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x00, //    34 '"'
+       0x00,0x00,0x14,0x7F,0x14,0x7F,0x14,0x00, //    35 '#'
+       0x00,0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00, //    36 '$'
+       0x00,0x02,0x45,0x32,0x08,0x26,0x51,0x20, //    37 '%'
+       0x00,0x32,0x4D,0x49,0x51,0x20,0x50,0x00, //    38 '&'
+       0x00,0x00,0x00,0x04,0x03,0x01,0x00,0x00, //    39 '''
+       0x00,0x00,0x00,0x1C,0x22,0x41,0x00,0x00, //    40 '('
+       0x00,0x00,0x00,0x41,0x22,0x1C,0x00,0x00, //    41 ')'
+       0x00,0x04,0x44,0x28,0x1F,0x28,0x44,0x04, //    42 '*'
+       0x00,0x08,0x08,0x08,0x7F,0x08,0x08,0x08, //    43 '+'
+       0x00,0x00,0x80,0x60,0x20,0x00,0x00,0x00, //    44 ','
+       0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08, //    45 '-'
+       0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00, //    46 '.'
+       0x00,0x00,0xC0,0x30,0x0C,0x03,0x00,0x00, //    47 '/'
+       0x00,0x3E,0x41,0x41,0x41,0x41,0x3E,0x00, //    48 '0'
+       0x00,0x00,0x00,0x02,0x7F,0x00,0x00,0x00, //    49 '1'
+       0x00,0x42,0x61,0x51,0x49,0x45,0x42,0x00, //    50 '2'
+       0x00,0x22,0x41,0x49,0x49,0x49,0x36,0x00, //    51 '3'
+       0x00,0x10,0x18,0x14,0x52,0x7F,0x50,0x00, //    52 '4'
+       0x00,0x4F,0x49,0x49,0x49,0x49,0x31,0x00, //    53 '5'
+       0x00,0x3C,0x4A,0x49,0x49,0x49,0x30,0x00, //    54 '6'
+       0x00,0x01,0x01,0x41,0x31,0x0D,0x03,0x00, //    55 '7'
+       0x00,0x36,0x49,0x49,0x49,0x49,0x36,0x00, //    56 '8'
+       0x00,0x06,0x49,0x49,0x49,0x29,0x1E,0x00, //    57 '9'
+       0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00, //    58 ':'
+       0x00,0x00,0x80,0x66,0x26,0x00,0x00,0x00, //    59 ';'
+       0x00,0x08,0x08,0x14,0x14,0x22,0x22,0x00, //    60 '<'
+       0x00,0x24,0x24,0x24,0x24,0x24,0x24,0x24, //    61 '='
+       0x00,0x22,0x22,0x14,0x14,0x08,0x08,0x00, //    62 '>'
+       0x00,0x00,0x02,0x01,0x51,0x09,0x06,0x00, //    63 '?'
+       0x00,0x1C,0x22,0x49,0x55,0x49,0x12,0x0C, //    64 '@'
+       0x00,0x40,0x70,0x1C,0x17,0x1C,0x70,0x40, //    65 'A'
+       0x00,0x7F,0x49,0x49,0x49,0x49,0x49,0x36, //    66 'B'
+       0x00,0x1C,0x22,0x41,0x41,0x41,0x41,0x22, //    67 'C'
+       0x00,0x7F,0x41,0x41,0x41,0x41,0x22,0x1C, //    68 'D'
+       0x00,0x7F,0x49,0x49,0x49,0x49,0x41,0x41, //    69 'E'
+       0x00,0x7F,0x09,0x09,0x09,0x09,0x01,0x01, //    70 'F'
+       0x00,0x1C,0x22,0x41,0x41,0x49,0x49,0x7A, //    71 'G'
+       0x00,0x7F,0x08,0x08,0x08,0x08,0x08,0x7F, //    72 'H'
+       0x00,0x00,0x41,0x41,0x7F,0x41,0x41,0x00, //    73 'I'
+       0x00,0x30,0x40,0x40,0x41,0x41,0x3F,0x00, //    74 'J'
+       0x00,0x7F,0x08,0x08,0x14,0x22,0x41,0x00, //    75 'K'
+       0x00,0x7F,0x40,0x40,0x40,0x40,0x40,0x00, //    76 'L'
+       0x00,0x7F,0x02,0x04,0x08,0x04,0x02,0x7F, //    77 'M'
+       0x00,0x7F,0x02,0x04,0x08,0x10,0x20,0x7F, //    78 'N'
+       0x00,0x1C,0x22,0x41,0x41,0x41,0x22,0x1C, //    79 'O'
+       0x00,0x7F,0x09,0x09,0x09,0x09,0x09,0x06, //    80 'P'
+       0x00,0x1C,0x22,0x41,0x41,0xC1,0xA2,0x9C, //    81 'Q'
+       0x00,0x7F,0x09,0x09,0x09,0x19,0x29,0x46, //    82 'R'
+       0x00,0x26,0x49,0x49,0x49,0x49,0x49,0x32, //    83 'S'
+       0x00,0x01,0x01,0x01,0x7F,0x01,0x01,0x01, //    84 'T'
+       0x00,0x3F,0x40,0x40,0x40,0x40,0x40,0x3F, //    85 'U'
+       0x00,0x01,0x07,0x18,0x60,0x18,0x07,0x01, //    86 'V'
+       0x00,0x7F,0x20,0x10,0x08,0x10,0x20,0x7F, //    87 'W'
+       0x00,0x41,0x22,0x14,0x08,0x14,0x22,0x41, //    88 'X'
+       0x00,0x01,0x02,0x04,0x78,0x04,0x02,0x01, //    89 'Y'
+       0x00,0x41,0x61,0x51,0x49,0x45,0x43,0x41, //    90 'Z'
+       0x00,0x00,0x00,0x00,0x7F,0x41,0x41,0x00, //    91 '['
+       0x00,0x00,0x03,0x0C,0x30,0xC0,0x00,0x00, //    92 '\'
+       0x00,0x00,0x41,0x41,0x7F,0x00,0x00,0x00, //    93 ']'
+       0x00,0x00,0x04,0x02,0x01,0x02,0x04,0x00, //    94 '^'
+       0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80, //    95 '_'
+       0x00,0x00,0x00,0x01,0x03,0x04,0x00,0x00, //    96 '`'
+       0x00,0x38,0x44,0x44,0x44,0x44,0x24,0x7C, //    97 'a'
+       0x00,0x7F,0x44,0x44,0x44,0x44,0x44,0x38, //    98 'b'
+       0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x00, //    99 'c'
+       0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x7F, //   100 'd'
+       0x00,0x38,0x54,0x54,0x54,0x54,0x54,0x18, //   101 'e'
+       0x00,0x04,0x7E,0x05,0x05,0x01,0x01,0x00, //   102 'f'
+       0x00,0x18,0xA4,0xA4,0xA4,0xA4,0xA4,0x7C, //   103 'g'
+       0x00,0x7F,0x04,0x04,0x04,0x04,0x04,0x78, //   104 'h'
+       0x00,0x00,0x44,0x44,0x7D,0x40,0x40,0x00, //   105 'i'
+       0x00,0x00,0x80,0x84,0x84,0x84,0x7D,0x00, //   106 'j'
+       0x00,0x7F,0x10,0x10,0x28,0x44,0x44,0x00, //   107 'k'
+       0x00,0x00,0x00,0x41,0x7F,0x40,0x00,0x00, //   108 'l'
+       0x00,0x7C,0x04,0x04,0x38,0x04,0x04,0x78, //   109 'm'
+       0x00,0x7C,0x08,0x04,0x04,0x04,0x04,0x78, //   110 'n'
+       0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38, //   111 'o'
+       0x00,0xFC,0x44,0x44,0x44,0x44,0x44,0x38, //   112 'p'
+       0x00,0x38,0x44,0x44,0x44,0x44,0x44,0xFC, //   113 'q'
+       0x00,0x00,0x7C,0x08,0x04,0x04,0x04,0x00, //   114 'r'
+       0x00,0x48,0x54,0x54,0x54,0x54,0x24,0x00, //   115 's'
+       0x00,0x04,0x04,0x3F,0x44,0x44,0x44,0x00, //   116 't'
+       0x00,0x3C,0x40,0x40,0x40,0x40,0x20,0x7C, //   117 'u'
+       0x00,0x04,0x0C,0x30,0x40,0x30,0x0C,0x04, //   118 'v'
+       0x00,0x3C,0x40,0x40,0x38,0x40,0x40,0x3C, //   119 'w'
+       0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44, //   120 'x'
+       0x00,0x1C,0xA0,0xA0,0xA0,0xA0,0x7C,0x00, //   121 'y'
+       0x00,0x44,0x64,0x54,0x54,0x4C,0x44,0x00, //   122 'z'
+       0x00,0x00,0x00,0x08,0x36,0x41,0x00,0x00, //   123 '{'
+       0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00, //   124 '|'
+       0x00,0x00,0x00,0x41,0x36,0x08,0x00,0x00, //   125 '}'
+       0x00,0x06,0x01,0x01,0x02,0x04,0x04,0x03, //   126 '~'
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..0b1a6f5
--- /dev/null
+++ b/main.c
@@ -0,0 +1,217 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Basic BDF Exporter - converts BDF files to C structures.
+       Only 8 bits wide fonts are supported.
+*/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bdf.h"
+#include "pi2c.h"
+#include "rterm.h"
+#include "ossd_i2c.h"
+
+#define DISPLAY_FONT 0x80000000
+
+/**
+ sarg: short argument
+ larg: long argument 
+ */
+static int arg_is(const char *arg, const char *sarg, const char *larg)
+{
+       if (sarg && (strcmp(arg, sarg) == 0))
+               return 1;
+       if (larg && (strcmp(arg, larg) == 0))
+               return 1;
+       return 0;
+}
+
+static void usage(const char *name)
+{
+       printf("%s [options] <bdf file>\n", name);
+       printf("  options are:\n");
+       printf("  header:     print file header\n");
+       printf("  verbose:    add extra info to the header\n");
+       printf("  line:       one line per glyph\n");
+       printf("  subset a-b: subset of glyphs to convert a to b, default 32-126\n");
+       printf("  all:        print all glyphs, not just 32-126\n");
+       printf("  native:     do not adjust font height 8 pixels\n");
+       printf("  ascender H: add extra ascender of H pixels per glyph\n");
+       printf("  rotate:     rotate glyphs' bitmaps CCW\n");
+       printf("  display A:  show converted font on SSD1306 compatible display\n");
+       printf("              using I2C bus 1, hexadecimal address A (default 3C)\n");
+       printf("  updown:     display orientation is upside down\n");
+}
+
+int main(int argc, char **argv)
+{
+       char *file;
+       bdfe_t *font;
+       int flags = 0;
+       uint8_t i2c_address = 0x3C;
+       uint8_t orientation = 0;
+       uint32_t gidx = 0;
+       unsigned ascender = 0;
+       unsigned gmin = 32, gmax = 126;
+
+       if (argc < 2) {
+               usage(basename(argv[0]));
+               return -1;
+       }
+
+       for(int i = 1; i < argc; i++) {
+               if (arg_is(argv[i], "-?", "help")) {
+                       usage(basename(argv[0]));
+                       return 0;
+               }
+
+               if (arg_is(argv[i], "-h", "header"))
+                       flags |= BDF_HEADER;
+
+               if (arg_is(argv[i], "-v", "verbose"))
+                       flags |= BDF_VERBOSE;
+
+               if (arg_is(argv[i], "-a", "ascender")) {
+                       if (i < argc && isdigit(*argv[i+1]))
+                               ascender = atoi(argv[++i]);
+               }
+
+               if (arg_is(argv[i], "-l", "line"))
+                       flags |= BDF_GPL;
+
+               if (arg_is(argv[i], "-s", "subset")) {
+                       if (i < argc && isdigit(*argv[i+1])) {
+                               i++;
+                               char *end;
+                               gmin = strtoul(argv[i], &end, 10);
+                               gmax = gmin;
+                               if (*end == '-')
+                                       gmax = strtoul(end+1, &end, 10);
+                               if (gmax < gmin) {
+                                       unsigned tmp = gmin;
+                                       gmin = gmax;
+                                       gmax = tmp;
+                               }
+                       }
+               }
+
+               if (arg_is(argv[i], "-a", "all")) {
+                       gmin = 0;
+                       gmax = 0xFFFFFFFF;
+               }
+
+               if (arg_is(argv[i], "-n", "native"))
+                       flags |= BDF_NATIVE;
+
+               if (arg_is(argv[i], "-r", "rotate"))
+                       flags |= BDF_ROTATE;
+
+               if (arg_is(argv[i], "-d", "display")) {
+                       flags |= DISPLAY_FONT;
+                       if (i < argc && isxdigit(*argv[i+1])) {
+                               i++;
+                               uint32_t i2ca = strtoul(argv[i], NULL, 16);
+                               if (i2ca && (i2ca < 0x78))
+                                       i2c_address = i2ca;
+                       }
+               }
+
+               if (arg_is(argv[i], "-u", "updown"))
+                       orientation = OSSD_UPDOWN;
+       }
+
+       file = argv[argc - 1];
+       font = bdf_convert(file, gmin, gmax, ascender, flags);
+
+       if (font == NULL) {
+               fprintf(stderr, "Unable to convert '%s'\n", file);
+               return -1;
+       }
+
+       if (!(flags & DISPLAY_FONT)) {
+               free(font);
+               return 0;
+       }
+
+       if (pi2c_open(PI2C_BUS) < 0) {
+               fprintf(stderr, "Unable to open i2c bus %d\n", PI2C_BUS);
+               free(font);
+               return -1;
+       }
+       pi2c_select(PI2C_BUS, i2c_address);
+
+       ossd_font_t of;
+       of.gw   = font->gw;
+       of.gh   = font->bpg;
+       of.go   = (uint8_t)gmin;
+       of.gn   = (uint8_t)font->chars;
+       of.font = font->font;
+
+       ossd_init(orientation);
+       ossd_set_user_font(&of, NULL);
+       ossd_select_font(OSSD_FONT_USER);
+
+       int gh = (of.gh + 7)/8; // glyph height in lines
+
+       char buf[16];
+       sprintf(buf, "%dx%d", of.gw, of.gh);
+       file = basename(file);
+       ossd_putlx(0, -1, file, OSSD_TEXT_REVERSE);
+       ossd_putlx(8 - gh, -1, buf, OSSD_TEXT_UNDERLINE | OSSD_TEXT_OVERLINE);
+       buf[1] = '\0';
+
+       stdin_mode(TERM_MODE_RAW);
+       fprintf(stderr, "Press any key to continue, 'q' to exit\n");
+       if (stdin_getch(-1) == 'q')     goto exit;
+
+       ossd_fill_screen(0);
+
+       do {
+               if (gidx > 0) {
+                       fprintf(stderr, "Press any key to continue, 'q' to exit\n");
+                       if (stdin_getch(-1) == 'q')     break;
+                       ossd_fill_screen(0);
+               }
+               for(int line = 0, l = 0; line < 8; line += gh, l++) {
+                       for(int i = 0; i < (128/of.gw) && gidx < font->chars; i++, gidx++) {
+                               buf[0] = gidx + of.go;
+                               ossd_putlx(line, i*of.gw, (const char *)buf, 0);
+                       }
+               }
+       } while(gidx < font->chars);
+
+exit:
+       stdin_mode(TERM_MODE_CAN);
+       pi2c_close(PI2C_BUS);
+       free(font);
+
+       return 0;
+}
diff --git a/ossd_i2c.c b/ossd_i2c.c
new file mode 100644 (file)
index 0000000..39eba4d
--- /dev/null
@@ -0,0 +1,390 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Limited set of functions for SSD1306 compatible OLED 128x64 displays
+       in text mode to minimize memory footprint if used on Atmel AVRs chips
+       with low memory.
+*/
+
+#include <stdio.h>
+
+#include "ossd_i2c.h"
+
+#if (OSSD_TARGET == OSSD_AVR) 
+       #include <avr/io.h>
+       #include <avr/pgmspace.h>
+       #include <util/atomic.h>
+       #include "i2cmaster.h"
+#else 
+       #define PROGMEM
+       #define pgm_read_byte(x) (*((uint8_t *)x))
+       #include <alloca.h>
+       #include <memory.h>
+#if (OSSD_TARGET == OSSD_RPI)
+       #include "pi2c.h"
+#endif
+#endif
+
+#define OSSD_CMD  0x00
+#define OSSD_DATA 0x40
+
+#define OSSD_SET_ADDR_MODE   0x20
+#define OSSD_ADDR_MODE_PAGE  0x10
+#define OSSD_ADDR_MODE_HOR   0x00
+#define OSSD_ADDR_MODE_VER   0x01
+
+#define OSSD_SET_COL_ADDR    0x21
+#define OSSD_SET_PAGE_ADDR   0x22
+
+#define OSSD_SET_START_LINE  0x40
+
+#define OSSD_SET_START_PAGE  0xB0
+#define OSSD_SET_START_LCOL  0x00
+#define OSSD_SET_START_HCOL  0x10
+
+#define OSSD_SET_MUX_RATIO   0xA8
+#define OSSD_SET_DISP_OFFSET 0xD3
+
+#define OSSD_SET_CONTRAST       0x81
+
+#define OSSD_SET_SEG_REMAP   0xA0
+#define OSSD_SEG_REMAP_R2L   0x01
+
+#define OSSD_SET_COM_DIR     0xC0
+#define OSSD_COM_DIR_UPDOWN  0x08
+
+#define OSSD_SET_COM_CONFIG  0xDA
+#define OSSD_COM_ALT         0x12
+#define OSSD_COM_LR_REMAP    0x22
+
+#define OSSD_SET_SLEEP_ON       0xAE
+#define OSSD_SET_SLEEP_OFF      0xAF
+
+#define OSSD_SET_INVERSE_ON     0xA7
+#define OSSD_SET_INVERSE_OFF 0xA6
+
+#define OSSD_SET_OUTPUT_RAM     0xA4
+#define OSSD_SET_OUTPUT_ON      0xA5
+
+#define OSSD_SET_DISP_CLOCK  0xD5
+
+#define OSSD_SET_PRECHARGE   0xD9
+
+#define OSSD_SET_VCOMH_LEVEL 0xDB
+#define OSSD_VCOMH_L065      0x00
+#define OSSD_VCOMH_L077      0x20
+#define OSSD_VCOMH_L083      0x30
+
+#define OSSD_SET_CHARGE_PUMP 0x8D
+#define OSSD_CHARGE_PUMP_ON  0x14
+#define OSSD_CHARGE_PUMP_OFF 0x10
+
+static const uint8_t font88[] PROGMEM = {
+#include "font88.h"
+};
+
+static const uint8_t font816[] PROGMEM = {
+#include "font816.h"
+};
+
+static ossd_font_t _ofont[OSSD_FONT_MAX+1] = {
+       {  8,  8, 32, 127-32, font88 },
+       {  8, 16, 32, 127-32, font816 },
+       {  0,  0, 0,       0, NULL }
+};
+
+static uint8_t _cfont;
+static uint8_t _mode;
+
+uint8_t ossd_select_font(uint8_t font)
+{
+       uint8_t fret = _cfont;
+       if (font <= OSSD_FONT_MAX)
+               _cfont = font;
+       return fret;
+}
+
+void ossd_set_user_font(ossd_font_t *nfont, ossd_font_t *ofont)
+{
+       if (ofont) {
+               ofont->gw = _ofont[OSSD_FONT_USER].gw;
+               ofont->gh = _ofont[OSSD_FONT_USER].gh;
+               ofont->go = _ofont[OSSD_FONT_USER].go;
+               ofont->gn = _ofont[OSSD_FONT_USER].gn;
+               ofont->font = _ofont[OSSD_FONT_USER].font;
+       }
+       _ofont[OSSD_FONT_USER].gw = nfont->gw;
+       _ofont[OSSD_FONT_USER].gh = nfont->gh; 
+       _ofont[OSSD_FONT_USER].go = nfont->go;
+       _ofont[OSSD_FONT_USER].gn = nfont->gn;
+       _ofont[OSSD_FONT_USER].font = nfont->font;
+}
+
+#if (OSSD_TARGET == OSSD_AVR)
+
+static void ossd_send_byte(uint8_t dc, uint8_t data)
+{
+       i2c_start(I2C_OSSD | I2C_WRITE);
+       i2c_write(dc);
+       i2c_write(data);
+       i2c_stop();
+}
+
+static void ossd_cmd_arg(uint8_t cmd, uint8_t arg)
+{
+       i2c_start(I2C_OSSD | I2C_WRITE);
+       i2c_write(OSSD_CMD);
+       i2c_write(cmd);
+       i2c_write(arg);
+       i2c_stop();
+}
+
+static void ossd_cmd_arg2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
+{
+       i2c_start(I2C_OSSD | I2C_WRITE);
+       i2c_write(OSSD_CMD);
+       i2c_write(cmd);
+       i2c_write(arg1);
+       i2c_write(arg2);
+       i2c_stop();
+}
+
+static void ossd_fill_line(uint8_t data, uint8_t num)
+{
+       i2c_start(I2C_OSSD | I2C_WRITE);
+       i2c_write(OSSD_DATA);
+       for(uint8_t i = 0; i < num; i++)
+               i2c_write(data);
+       i2c_stop();
+}
+
+#else
+
+static void ossd_send_byte(uint8_t dc, uint8_t data)
+{
+       uint8_t buf[2];
+       buf[0] = dc;
+       buf[1] = data;
+
+       pi2c_write(PI2C_BUS, buf, 2);
+}
+
+static void ossd_cmd_arg(uint8_t cmd, uint8_t arg)
+{
+       uint8_t data[3];
+       data[0] = OSSD_CMD;
+       data[1] = cmd;
+       data[2] = arg;
+       pi2c_write(PI2C_BUS, data, 3);
+}
+
+static void ossd_cmd_arg2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
+{
+       uint8_t data[4];
+       data[0] = OSSD_CMD;
+       data[1] = cmd;
+       data[2] = arg1;
+       data[3] = arg2;
+       pi2c_write(PI2C_BUS, data, 4);
+}
+
+static void ossd_fill_line(uint8_t data, uint8_t num)
+{
+       uint8_t *buf = (uint8_t *)alloca(num+1);
+       memset(buf, data, num+1);
+       buf[0] = OSSD_DATA;
+       pi2c_write(PI2C_BUS, buf, num+1);
+}
+
+#endif
+
+static inline void ossd_cmd(uint8_t cmd)
+{
+       ossd_send_byte(OSSD_CMD, cmd);
+}
+
+static inline void ossd_data(uint8_t data)
+{
+       ossd_send_byte(OSSD_DATA, data);
+}
+
+static uint8_t ossd_set_addr_mode(uint8_t set_mode)
+{
+       uint8_t ret = _mode;
+       if (_mode != set_mode) {
+               ossd_cmd_arg(OSSD_SET_ADDR_MODE, set_mode);
+               // if switching back to page mode
+               // set full screen as output region
+               if (set_mode == OSSD_ADDR_MODE_PAGE) {
+                       ossd_cmd_arg2(OSSD_SET_PAGE_ADDR, 0, 7);
+                       ossd_cmd_arg2(OSSD_SET_COL_ADDR, 0, 127);
+               }
+               _mode = set_mode;
+       }
+       return ret;
+}
+
+void ossd_goto(uint8_t line, uint8_t x)
+{
+       if (_mode == OSSD_ADDR_MODE_PAGE) {
+               ossd_cmd(OSSD_SET_START_PAGE | (line & 0x07));
+               ossd_cmd(OSSD_SET_START_LCOL | (x & 0x0F));
+               ossd_cmd(OSSD_SET_START_HCOL | (x >> 4));
+       }
+       else {
+               uint8_t gw = _ofont[_cfont].gw;
+               // in OSSD_ADDR_MODE_HOR/VER mode we set output region (gw x 16)
+               ossd_cmd_arg2(OSSD_SET_COL_ADDR, x, x + gw - 1);
+               ossd_cmd_arg2(OSSD_SET_PAGE_ADDR, line, line+1);
+       }
+}
+
+void ossd_fill_screen(uint8_t data)
+{
+       // fill full screen line by line
+       for(uint8_t line = 0; line < 8; line++) {
+               ossd_goto(line, 0);
+               ossd_fill_line(data, 128);
+       }
+} 
+
+void ossd_sleep(uint8_t on_off)
+{
+       if (on_off)
+               ossd_cmd(OSSD_SET_SLEEP_ON);
+       else
+               ossd_cmd(OSSD_SET_SLEEP_OFF);
+}
+
+void ossd_set_contrast(uint8_t val)
+{
+       ossd_cmd_arg(OSSD_SET_CONTRAST, val);
+}
+
+static void ossd_put_centre(uint8_t line, const char *str, uint8_t atr)
+{
+       uint16_t len;
+       uint8_t x = 0;
+       uint8_t gw = _ofont[_cfont].gw;
+       uint8_t gh = _ofont[_cfont].gh;
+       for(len = 0; str[x]; len+=gw, x++);
+       if (len > 128)
+               x = 0;
+       else
+               x = (128 - len) / 2;
+
+       // in case if new text is shorter than previous one
+       // we clean line up to x position
+       if (x) {
+               for(uint8_t i = 0; i < (gh+7)/8; i++) {
+                       ossd_goto(line + i, 0);
+                       ossd_fill_line(0, x);
+               }
+       }
+
+       // recursive call of ossd_putlx()
+       ossd_putlx(line, x, str, atr);
+
+       // in case if new text is shorter than previous one
+       // we clean to the end of the line
+       if (x) {
+               for(uint8_t i = 0; i < (gh+7)/8; i++) {
+                       ossd_goto(line + i, x + len);
+                       ossd_fill_line(0, x);
+               }
+       }
+}
+
+void ossd_putlx(uint8_t line, int8_t x, const char *str, uint8_t atr)
+{
+       line &= 0x07;
+
+       // try to put this text in the middle of the line:
+       // ossd_put_centre() will calculate proper x coordinate
+       if (x < 0) {
+               ossd_put_centre(line, str, atr);
+               return;
+       }
+
+       uint8_t rev = 0;
+       uint8_t over = 0;
+       uint8_t under = 0;
+       if (atr & OSSD_TEXT_REVERSE)
+               rev = ~rev;
+       if (atr & OSSD_TEXT_OVERLINE)
+               over = 0x01;
+       if (atr & OSSD_TEXT_UNDERLINE)
+               under = 0x80;
+       
+       uint8_t gw = _ofont[_cfont].gw;
+       uint8_t gh = _ofont[_cfont].gh;
+       uint8_t go = _ofont[_cfont].go;
+       const uint8_t *font = _ofont[_cfont].font;
+       uint8_t cmode = ossd_set_addr_mode(OSSD_ADDR_MODE_HOR);
+       for(; *str != '\0'; str++, x += gw) {
+               uint16_t idx = (*str - go) * gh;
+               if ((uint8_t)x > (128 - gw)) {
+                       x = 0;
+                       line = (line + (gh+7)/8) & 0x07;
+               }
+               ossd_goto(line, x);    
+               for(uint8_t i = 0; i < gh; i++) {
+                       uint8_t d = pgm_read_byte(&font[idx+i]);
+                       d ^= rev;
+                       if (under && (gh == 8 || i > (gw - 1)))
+                               d ^= under;
+                       if (i < gw)
+                               d ^= over;
+                       ossd_data(d);
+               }
+       }
+       ossd_set_addr_mode(cmode);
+}
+
+void ossd_init(uint8_t orientation)
+{
+       _mode = 0xFF;
+       // set all default values
+       ossd_cmd(OSSD_SET_SLEEP_ON);
+       ossd_cmd_arg(OSSD_SET_MUX_RATIO, 63);   
+       ossd_cmd_arg(OSSD_SET_DISP_OFFSET, 0);
+       ossd_cmd(OSSD_SET_START_LINE | 0);
+       ossd_cmd(OSSD_SET_SEG_REMAP | (orientation & OSSD_SEG_REMAP_R2L));
+       ossd_cmd(OSSD_SET_COM_DIR | (orientation & OSSD_COM_DIR_UPDOWN));
+       ossd_cmd_arg(OSSD_SET_COM_CONFIG, OSSD_COM_ALT);
+       ossd_cmd_arg(OSSD_SET_CONTRAST, 0x7F);
+       ossd_cmd(OSSD_SET_OUTPUT_RAM);
+       ossd_cmd_arg(OSSD_SET_DISP_CLOCK, 0x80);
+       ossd_cmd_arg(OSSD_SET_PRECHARGE, 0x22);
+       ossd_cmd_arg(OSSD_SET_VCOMH_LEVEL, OSSD_VCOMH_L077);
+       ossd_cmd(OSSD_SET_INVERSE_OFF);
+       ossd_cmd_arg(OSSD_SET_CHARGE_PUMP, OSSD_CHARGE_PUMP_ON);
+       ossd_set_addr_mode(OSSD_ADDR_MODE_PAGE);
+       ossd_fill_screen(0);
+       ossd_cmd(OSSD_SET_SLEEP_OFF);
+       ossd_goto(0, 0);
+}
diff --git a/ossd_i2c.h b/ossd_i2c.h
new file mode 100644 (file)
index 0000000..747528f
--- /dev/null
@@ -0,0 +1,122 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef __OLED_SSD1306_I2C__
+#define __OLED_SSD1306_I2C__
+
+/**
+       Limited set of functions for SSD1306 compatible OLED displays in text mode
+       to minimize memory footprint if used on Atmel AVRs with low memory.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} // dummy bracket for Visual Assist
+#endif
+#endif
+
+/** Target platform */
+#define OSSD_AVR        1 /*< AVR compiler */
+#define OSSD_RPI        2 /*< Raspberry Pi */
+#define OSSD_GALILEO 3 /*< Reserved for Intel Galileo */
+
+#ifdef __AVR_ARCH__
+       #define OSSD_TARGET OSSD_AVR
+#else
+       #define OSSD_TARGET OSSD_RPI
+#endif
+
+#if (OSSD_TARGET == OSSD_AVR)
+       #define I2C_OSSD (0x3C << 1)
+#else
+       #include <stdint.h>
+#endif
+
+#define OSSD_FONT_8x8  0
+#define OSSD_FONT_8x16 1
+#define OSSD_FONT_USER 2
+#define OSSD_FONT_MAX  OSSD_FONT_USER
+
+typedef struct ossd_font_s
+{
+       uint8_t gw; /*< glyph width  */
+       uint8_t gh; /*< glyph height */
+       uint8_t go; /*< font offset, first glyph index */
+       uint8_t gn; /*< number of glyphs presented */
+       const uint8_t *font;
+} ossd_font_t;
+
+/** 
+  flat cable connected at the top
+  use ossd_init(OSSD_UPDOWN) to rotate screen
+  */
+#define OSSD_UPDOWN 0x09
+
+/** set default parameters */
+void ossd_init(uint8_t orientation);
+
+/** fill screen with specified pattern */
+void ossd_fill_screen(uint8_t data);
+
+/** set display to sleep mode */
+void ossd_sleep(uint8_t on_off);
+
+/** set display contrast */
+void ossd_set_contrast(uint8_t val);
+
+/** select one of three fonts for following ossd_putlx() calls */
+uint8_t ossd_select_font(uint8_t font);
+
+/** 
+ set user font selectable by OSSD_FONT_USER to nfont
+ store current user font in ofont (if not NULL)
+ */
+void ossd_set_user_font(ossd_font_t *nfont, ossd_font_t *ofont);
+
+/** text attributes */
+#define OSSD_TEXT_REVERSE   0x01
+#define OSSD_TEXT_UNDERLINE 0x02
+#define OSSD_TEXT_OVERLINE  0x04
+
+/**
+ output string up to 64 chars in length
+ line: 0-7
+ x:    0-127, or -1 for centre of the line
+ str:  output string
+ atr:  OSSD_TEXT_*
+ */
+void ossd_putlx(uint8_t line, int8_t x, const char *str, uint8_t atr);
+
+/** void screen */
+static inline void ossd_cls(void) {
+       ossd_fill_screen(0);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pi2c.c b/pi2c.c
new file mode 100644 (file)
index 0000000..518af4c
--- /dev/null
+++ b/pi2c.c
@@ -0,0 +1,102 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Basic I2C wrapper for Raspberry Pi, only write is supported.
+
+       Make sure that RPi i2c driver is enabled, check following files:
+       /etc/modprobe.d/raspi-blacklist.conf
+               #blacklist i2c-bcm2708
+       /etc/modules
+               i2c-dev
+       /etc/modprobe.d/i2c.conf
+               options i2c_bcm2708 baudrate=400000
+*/
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+
+#include "pi2c.h"
+
+/** i2c bus file descriptors */
+static int i2c_bus[2] = { -1, -1 };
+
+/** open I2C bus if not opened yet and store file descriptor */
+int pi2c_open(uint8_t bus)
+{
+       char bus_name[64];
+
+       if (bus > PI2C_BUS1)
+               return -1;
+
+       // already opened?
+       if (i2c_bus[bus] >= 0)
+               return 0;
+
+       // open i2c bus and store file descriptor
+       sprintf(bus_name, "/dev/i2c-%u", bus);
+
+       if ((i2c_bus[bus] = open(bus_name, O_RDWR)) < 0)
+               return -1;
+
+       return 0;
+}
+
+/** close I2C bus file descriptor */
+int pi2c_close(uint8_t bus)
+{
+       if (bus > PI2C_BUS1)
+               return -1;
+
+       if (i2c_bus[bus] >= 0)
+               close(i2c_bus[bus]);
+       i2c_bus[bus] = -1;
+
+       return 0;
+}
+
+/** select I2C device for pi2c_write() calls */
+int pi2c_select(uint8_t bus, uint8_t slave)
+{
+       if ((bus > PI2C_BUS1) || (i2c_bus[bus] < 0))
+               return -1;
+
+       return ioctl(i2c_bus[bus], I2C_SLAVE, slave);
+}
+
+/** write to I2C device selected by pi2c_select() */
+int pi2c_write(uint8_t bus, const uint8_t *data, uint32_t len)
+{
+       if ((bus > PI2C_BUS1) || (i2c_bus[bus] < 0))
+               return -1;
+
+       if (write(i2c_bus[bus], data, len) != (ssize_t)len)
+               return -1;
+
+       return 0;
+}
diff --git a/pi2c.h b/pi2c.h
new file mode 100644 (file)
index 0000000..50d0691
--- /dev/null
+++ b/pi2c.h
@@ -0,0 +1,61 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Basic I2C wrapper for Raspberry Pi, only write is supported.
+
+       Make sure that RPi i2c driver is enabled, check following files:
+       /etc/modprobe.d/raspi-blacklist.conf
+               #blacklist i2c-bcm2708
+       /etc/modules
+               i2c-dev
+       /etc/modprobe.d/i2c.conf
+               options i2c_bcm2708 baudrate=400000
+*/
+
+#ifndef __PI2C_H__
+#define __PI2C_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PI2C_BUS0 0 /*< P5 header I2C bus */
+#define PI2C_BUS1 1 /*< P1 header I2C bus */
+#define PI2C_BUS  PI2C_BUS1 // default bus
+
+int pi2c_open(uint8_t bus);  /*< open I2C bus  */
+int pi2c_close(uint8_t bus); /*< close I2C bus */
+int pi2c_select(uint8_t bus, uint8_t slave); /*< select I2C slave */
+int pi2c_write(uint8_t bus, const uint8_t *data, uint32_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/rterm.c b/rterm.c
new file mode 100644 (file)
index 0000000..4414d56
--- /dev/null
+++ b/rterm.c
@@ -0,0 +1,90 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Sets stdin to raw mode so read() can get chars one by one 
+       without waiting for the Enter
+*/
+#include <poll.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "rterm.h"
+
+static int restore = 0;
+
+/** restore canonical mode ar exit */
+static void    mode_restore_proc(void)
+{
+       stdin_mode(TERM_MODE_CAN);
+}
+
+/** set terminal mode: RAW or CANonical */
+int stdin_mode(int mode)
+{
+       struct termios term_attr;
+
+       tcgetattr(STDIN_FILENO, &term_attr);
+       /* set the terminal to raw mode */
+       if (mode == TERM_MODE_RAW) {
+               term_attr.c_lflag &= ~(ECHO | ICANON);
+               term_attr.c_cc[VMIN] = 0;
+               if (!restore) {
+                       restore++;
+                       atexit(mode_restore_proc);
+               }
+       }
+       else {
+               term_attr.c_lflag |= (ECHO | ICANON);
+               term_attr.c_cc[VMIN] = 1;
+       }
+       tcsetattr(STDIN_FILENO, TCSANOW, &term_attr);
+
+       return mode;
+}
+
+/** read stdin with timeout in ms, or -1 until a key is pressed */
+int stdin_getch(int timeout)
+{
+       struct pollfd polls;
+       polls.fd = STDIN_FILENO;
+       polls.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+       polls.revents = 0;
+
+       if (poll(&polls, 1, timeout) < 0)
+               return -1;
+
+       int ch = 0;
+       if (polls.revents) {
+               if (read(STDIN_FILENO, &ch, 1) < 0)
+                       return -1;
+       }
+
+       return ch;
+}
diff --git a/rterm.h b/rterm.h
new file mode 100644 (file)
index 0000000..d73f042
--- /dev/null
+++ b/rterm.h
@@ -0,0 +1,42 @@
+/*  BSD License
+    Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+       OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+       POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+       Sets stdin to raw mode so read() can get chars one by one 
+       without waiting for the Enter
+*/
+#ifndef __RAW_STDIN_TERM_H__
+#define __RAW_STDIN_TERM_H__
+
+#define TERM_MODE_CAN 0 /*< canonical terminal mode */
+#define TERM_MODE_RAW 1 /*< raw terminal mode */
+
+/** set terminal mode: RAW or CANonical */
+int    stdin_mode(int mode);
+/** read stdin with timeout in ms, or -1 until a key is pressed */
+int stdin_getch(int timeout);
+
+#endif