/* Inverted text in 11 bytes of RAM, with size-optimized code.
To compile and run:
gcc -std=c99 invertext.c && ./a.out
This program generates an image of text one pixel line at a time,
starting from the bottom, without using a framebuffer. Indeed, the
only arrays it uses at all are the multiline input character string to
be printed (backwards) and the constant font data itself. As an
example it uses “##” and ” ” for the pixels, but in the actual
program, it would use some interface for sending pixel data one pixel
or line of pixels at a time.
How much resources does this need? The font data is 288 bytes.
avr-gcc 4.3.5 invoked as `avr-gcc -g -Os -std=c99 -mmcu=atmega168 -c
-Wall invertext.c` generates a .o file with 288 bytes of .progmem.data and
about 288 bytes of .text, i.e. machine code.
This is small enough to run on an ATTiny9 or ATTiny10, although it
omits fputs/fputc and whatever you’d actually use to replace them in a
real system, plus the avrlibc startup code. It doesn’t quite fit in
an ATTiny4 or ATTiny5. (And GCC is generating ATMega code, which uses
instructions the tiny brain-dead AVRs don’t support.)
The call graph is:
main -> print_pixels -> print_line -> get_pixels
-> prev_line
But it inlines everything into the single print_pixels function, which doesn’t
use any RAM internally, but pushes a few registers on the stack, using a few
more bytes of RAM. The 6 bytes of `.data` are the two strings passed to fputs,
which are really just a placeholder for a real pixel output function.
*/
#include <stdio.h>
#ifdef __AVR
/* To compile for AVR:
avr-gcc -Os -std=c99 -mmcu=atmega168 -c -Wall -Wa,-adhlns=invertext.lst \
invertext.c
*/
#include <avr/pgmspace.h>
#else
#define PROGMEM
#define pgm_read_byte(x) (*(x))
#endif
/* The font; slightly modified XBM format. Six rows of 16 fixed-size
* glyphs. */
enum {
font_width = 64,
font_height = 36,
chars_per_line = 16,
lines = 6,
char_width = font_width / chars_per_line,
char_height = font_height / lines,
};
static const char font_bits[] PROGMEM = {
0xDF, 0xAA, 0xED, 0xDD, 0xEB, 0xDA, 0xFF, 0xBF, 0xDF, 0x0A, 0xB8, 0xDA,
0xDD, 0xDD, 0xFF, 0xBF, 0xDF, 0xAF, 0xDC, 0xFD, 0xDD, 0x88, 0x8F, 0xDF,
0xFF, 0x0F, 0xEB, 0xFA, 0xDD, 0xDD, 0xFF, 0xEF, 0xDF, 0xAF, 0xB8, 0xF5,
0xDD, 0xDA, 0xFD, 0xED, 0xFF, 0xFF, 0xFD, 0xFF, 0xEB, 0xFF, 0xFE, 0xFF,
0xDD, 0x8D, 0x8A, 0x89, 0xD9, 0xFF, 0xFB, 0xCE, 0xCA, 0xBA, 0xEA, 0xBE,
0xAA, 0xDD, 0x8D, 0xBD, 0xD8, 0xDB, 0xC8, 0xDC, 0x9D, 0xFF, 0xFE, 0xDB,
0xDA, 0xBD, 0xBB, 0xEA, 0xBA, 0xFF, 0x8D, 0xFD, 0x8D, 0xC8, 0xCB, 0xED,
0xCC, 0xDD, 0xFB, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF,
0xD9, 0xDC, 0x8C, 0x98, 0x8A, 0xAB, 0xAE, 0xDB, 0xAA, 0xAA, 0xEA, 0xEE,
0xDA, 0xAB, 0x8E, 0xAA, 0x8A, 0xEC, 0xCA, 0xAC, 0xD8, 0xCB, 0x8E, 0xA8,
0xAE, 0xAA, 0xEA, 0xAE, 0xDA, 0xAA, 0xAE, 0xAA, 0xA9, 0xDC, 0x8C, 0x9E,
0x8A, 0xAD, 0xA8, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDC, 0x9C, 0xA8, 0xAA, 0xAA, 0x98, 0xCE, 0xFD, 0xAA, 0xEA, 0xAD, 0xAA,
0xAA, 0xDB, 0xDE, 0xFA, 0xAC, 0xDC, 0xAD, 0x8A, 0xDD, 0xDD, 0xDD, 0xFF,
0xAE, 0xBA, 0xAD, 0x88, 0xDA, 0xDE, 0xDB, 0xFF, 0xDE, 0xCA, 0x8D, 0xAD,
0xDA, 0x98, 0xCB, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F,
0xFD, 0xFE, 0xFB, 0xFB, 0xDE, 0xEB, 0xFC, 0xFF, 0x9B, 0x9C, 0xD9, 0x9D,
0xFC, 0xAF, 0xAD, 0xDC, 0xAF, 0xEA, 0xAA, 0xA8, 0xCA, 0xCB, 0x8D, 0xAA,
0xAF, 0xEA, 0xCA, 0x9D, 0xDA, 0xAB, 0xAD, 0xAA, 0x9F, 0x9C, 0x99, 0xBD,
0x8A, 0xAB, 0xA8, 0xDA, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFC, 0xFF, 0xFF,
0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xBF, 0xED, 0xF5, 0x9C, 0x9A, 0xA8, 0xAA,
0xAA, 0xD8, 0xDD, 0xFA, 0xAA, 0xCC, 0xAD, 0xAA, 0xAD, 0xCB, 0x9D, 0xFF,
0xAC, 0xBE, 0xAD, 0x8A, 0x9D, 0xDD, 0xDD, 0xFF, 0x9E, 0xCE, 0x9B, 0xAD,
0xBA, 0xD8, 0xDD, 0xFF, 0xBE, 0xFF, 0xFF, 0xFF, 0xCF, 0xBF, 0xEF, 0xFF,
};
/* An integral type of at least char_width+1 bits. */
typedef char pixel_slice;
/* e.g. ones(5) is 11111 in binary. */
static inline int ones(int bitshift)
{
return (1 << bitshift) - 1;
}
/* This returns a row of char_width pixels from the specified character.
*/
static pixel_slice get_pixels(char c, int pixel_row)
{
unsigned char glyph = c - 32;
if (glyph > chars_per_line * lines) glyph = 0; /* space character */
unsigned char line = glyph / chars_per_line;
unsigned char offset = glyph % chars_per_line;
int pix_offset = (line * char_height + pixel_row) * font_width +
offset * char_width;
/* XXX note that this assumes the pixels are all in the same byte!
* This effectively limits us to 4- or 8-pixel-wide fonts. */
return (pgm_read_byte(&font_bits[pix_offset >> 3])
>> (pix_offset & ones(3)))
& ones(char_width);
}
/* prev_line: Given a pointer t to the \0 at the end of a string s or
to the first character after a \n in it, return a pointer to the
first character after the previous \n or, if none, to the first
character of the string. */
static char *prev_line(char *s, char *t)
{
if (t == s) return t; /* The string may be empty! */
do {
t--;
} while (t != s && t[-1] != '\n');
return t;
}
enum { output_width = 40 };
/* Takes a pointer to the byte past the \n or \0 terminating the line.
* Returns a pointer to the byte beginning the line.
*/
static char *print_line(char *string, char *end)
{
char *line = prev_line(string, end);
unsigned char width = end - line - 1; /* Assuming line lengths < 256! */
unsigned char padding = output_width / char_width - width;
char pixel_row = char_height;
do {
pixel_row--;
char *s = end - 1;
unsigned char padding_left = padding;
for (unsigned char i = 0; i < output_width / char_width; i++) {
if (padding_left) {
padding_left--;
} else {
s--;
}
pixel_slice p = get_pixels(*s, pixel_row);
for (char j = char_width; j; j--) {
p <<= 1;
fputs((1 << char_width) & p ? " " : "##", stdout);
}
}
fputs("\n", stdout);
} while (pixel_row);
return line;
}
void print_pixels(char *string)
{
char *line = string;
while (*line) line++;
line++; /* Get a pointer one past the \0. */
while (line != string) line = print_line(string, line);
}
#ifndef __AVR
int main()
{
print_pixels("Hello,\nWeirdos");
}
#endif
--
To unsubscribe: http://lists.canonical.org/mailman/listinfo/kragen-hacks