§ Код функции

Здесь используется prn для вывода на экран. В данном случае — не реализована, оставлена на дальнейшее усмотрение и как няшная заглушка ^_^.
void print(const char* s) {

    int i = 0, n = 0;
    while (s[i]) {
        byte ch = s[i++];
        if (ch == 0xD0) { // Главный набор
            ch = s[i++];
            ch = (ch == 0x81 ? 0xF0 : ch - 0x10);
        } else if (ch == 0xD1) { // Вторичный набор
            ch = s[i++];
            ch = (ch == 0x91 ? 0xF1 : ch + (ch < 0xB0 ? 0x60 : 0x10));
        }
        prn(ch);
        n++;
    }
}

§ Для AVR-контроллера

Помимо обычного вывода символов, существует также ESC-код 1Bh [ATTR], который позволяет менять цвет дальнейших символов на лету. Переменная cursor_attr должна быть объявлена.
void print(const char* s, byte pgm = 0) {

    byte ch, nx, ct;
    int  i = 0, n = 0;
    while ((ch = (pgm ? pgm_read_byte(&s[i++]) : s[i++]))) {

        ct = (ch == 0x1B);

        // Считывание дополнительного байта UTF8 или CTL
        if (ch == 0xD0 || ch == 0xD1 || ch == 0x1B) {

            // Прочитать следующий символ
            nx = (pgm ? pgm_read_byte(&s[i++]) : s[i++]);

            // Разбор дополнительного байта
            if      (ch == 0xD0) { ch = (nx == 0x81 ? 0xF0 : nx - 0x10); }
            else if (ch == 0xD1) { ch = (nx == 0x91 ? 0xF1 : nx + (nx < 0xB0 ? 0x60 : 0x10)); }
            else if (ct) { cursor_attr = nx; continue; }
        }

        prn(ch);
        n++;
    }
}