/* 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