КОТ++
Прикладное программирование
Системное программирование
Программирование микроконтроллеров

Программирование — это не наука, а ремесло. (c) Ричард Столлман

Опубликовано
Комментарии 0

И так мне понадобилось разобрать данные из файла шрифта woff (Web Open Font Format). Я прям с самого начала скажу, что там чёрт ногу сломит, а разработчики формата это представители инопланетной жизни страдающие от старческого маразма. Отправляемся на https://www.w3.org/TR/WOFF/, читаем, в принципе всё понятно (нет). Если кратко, то это бинарный файл, который имеет заголовок с информацией о таблицах. Таблицы – это некоторые структурированные данные, информация в которых отражает только один технический аспект шрифта. И все эти таблицы в woff заархивированы DEFLATE алгоритмом сжатия. На картинках ниже формат woff файла представлен более подробно.

Я буду рассматривать в этой статье только таблицы cmap и CFF, так как основная информация о символах именно в них.

cmap таблица по сути содержит информацию о том, как перейти от unicode байт к глифам (к начертаниям). По каким-то неведомым причинам видов cmap таблицы существует аж 14 штук, что за балаган? Но мне нужен только 4-ый. Документация просто ужасная, но методом проб и ошибок я пришёл к правильному способу перехода от unicode к глифам. Мне повезло в том, что количество сегментов равнялось количеству символов, и соответственно каждый startCode равнялся коду каждого символа. Но glyphIdArray почему-то был пустой. В итоге я сделал так: к startCode прибавил idDelta, и отсортировал полученные результаты по порядку. Этот порядок строго соответствовал порядку глифов в CFF. Може я сделал не совсем правильно, но иного способа не нашёл. Потому что везде пишут о том, что для этой задачи нужны ещё таблицы glyf и loca, но в woff файлах я этих таблиц не видел.

Перейдём к CFF. Если вы открыли документацию по CFF, то думаю вы поняли, что особо ничего и не понятно. CFF таблица состоит из заголовка, индексов (INDEX) и словарей (DICT). Индексами названы данные, которые структурированы как массив, словарями названы данные, которые структурированы как ключ-значение данные. В CFF присутствуют следующие данные: NameIndex, TopDictIndex, StringIndex, GlobalSubrIndex, Charsets, Encodings, CharStringIndex, PrivateDict, FontDictIndex, LocalSubrIndex. Мне понадобилось разобрать именно CharStringIndex, но так как данные предусматривалось находить по offset’ам, а offset говорит о нахождении только следующего блока, то разбирать пришлось практически всю CFF таблицу, чтобы дойти до CharStringIndex. Для идентификации глифов из CharStringIndex я просто сранивал длину каждого CharString из этого массива, дальше расковыривать формат не требовалось.

Кстати, в процессе поиска информации о форматах нашёл блог одного очень умного азиата, его исходники мне очень помогли в работе, парень проделал огромную работу по разбору шрифтов. В его исходниках всё понятно, написано очень классно, в отличие от мудацких исходников opentype.js, по которым понятно только одно, что автору платили за кривость кода.

В заключении вернусь к вопросу о том, что почему же woff формат такой сложный? Этот формат несёт в себе наследие CFF формата. CFF насколько я понимаю по документации был разработан в 2003 году, и вероятно он тоже в себе таит тайны прошлого. Прошлого, когда был важен каждый байт и важна скорость загрузки информации в память (а самая быстрая загрузка это когда просто копирование последовательности байт в память), видимо поэтому вся информация о шрифтах структурировалась в бинарный вид, вид который хорошо понятен машине, а не человеку – это вам не json смотреть.

Теги , ,
Автор

Опубликовано
Комментарии 0

В этом посте я хочу озвучить что же не так со многими скриптовыми языками. Я очень давно не люблю что JavaScript, что Python, что PHP, но какое-то время не мог чётко сформулировать причину. И вот недавно разбираясь в исходниках opentype.js, я полностью понял что же не так с этими скриптовыми языками.

Главный аспект программирования это управление сложностью, то есть код должен быть простым и понятным для чтения. А значит разрабатывая проект программист должен уменьшать сложность конкретных участков кода, чтобы логика каждого просматриваемого участка кода была понятной другому человеку. Это как жонглирование, или же жонглировать 20-ю яблоками, или например 7-ю, разница почти в три раза. Также и в программировании, уменьшая количество сущностей, которые нужно держать в голове в один момент времени упрощается мысленное жонглирование. По некоторым исследованиям говорят, что оптимальное количество сущностей должно быть не более 7-и, по этой причине, например, советуют не использовать в одном методе более 7 переменных.

Поэтому от профессионала требуется определённая дисциплина, чтобы писать код понятный не только машине, но и другим людям. Многие языки в этом помогают, например наличие классов, наличие строгой типизации, наличие хорошего синтаксиса приближенного к человеческому восприятию и так далее. Так вот JavaScript не имеет из этого ничего, в результате мы имеем очень низкий порог входа, что очень радует людей по складу ума далёких от программирования и в добавок к этому получаем кучу некачественного кода. Да, на JavaScript можно писать хорошо, можно в имена переменных добавлять постфикс или префикс для обозначения типа, эмулировать классы (но про интерфейсы придётся забыть), не злоупотреблять динамической типизацией и прочее. Но для этого человек должен быть сильно дисциплинированным, и вот парадокс – хорошо дисциплинированный человек не посмотрит в сторону JavaScript, Python, PHP и прочего. Разжёвывать дальше недостатки, которыми болеют Python и PHP, у меня нет желания, мысль и так ясна.

Такие языки будут привлекать только людей, которые не испытывают тягу к программированию по-настоящему, а лишь людей с тягой к деньгам, ведь JavaScript и Python на мой взгляд очень раскручены корпорациями и пузырь зарплат разработчиков на скриптовых языках чрезмерно надут. И как вы думаете какого качества проекты в итоге получатся от выше описанных кодеров?

Теги ,
Автор

Опубликовано
Комментарии 0

Как-то много раз передо мной мелькали различные статьи по поводу языка Haxe, описывали его достаточно красочно, и я очень сильно загорелся попробовать что это такое собственно. Для тех кто совсем не в курсе: Haxe позволяет транслировать свой код в код других языков (php, javascript, flash, c++, java, c#). Тем самым достигается мультиплатформенность. На словах, конечно, очень всё красиво и замечательно. Но на деле всё несколько иначе.

Чтобы плотно познакомиться с этим языком, я решил, что возьму свой старый flash проект и попробую переделать на Haxe. Почему flash? Да потому, что синтаксис Haxe максимально близок к синтаксису ActionScript 3, так заявлено разработчиками, на деле же есть множество маленьких несостыковок, из-за которых пришлось конкретно промучаться при переделывании проекта. Обрадовало, что для Haxe есть очень хорошая среда HaxeDevelop, это практически копия среды FlashDevelop, за которой я создал несколько игр, так что было очень приятно увидеть уже знакомый интерфейс.

И так, я беру проект Hot Jihod и начинаю пытаться собрать его под Haxe. Также очень круто, что под Haxe есть библиотека OpenFL, которая практически повторяет Flash, что существенно упрощает работу по сборке проекта в Haxe. Но на деле я промучался с жутким синтаксисом языка Haxe, меня очень расстроили циклы, синтаксис циклов for очень стал далёк от синтаксиса циклов for в C/C++, я считаю, что такие циклы for как в Haxe очень понятны и удобны новичкам в программировании, а для тех кто выучил C/C++ такой синтаксис только раздражает.

for (var i: int = 0; i < rest.length; i++) //AS3 синтаксис 
for (i in 0…rest.length) //синтаксис Haxe, 1-ый вариант
for (item in rest) //синтаксис Haxe, 2-ой вариант

Кто-то скажет: «что в Haxe то лучше!». Возможно в каком-то смысле так и есть. Но мне не по душе такие циклы, да и к тому же пришлось ходить по всему коду и исправлять циклы на Haxe вариант. И ещё на самом деле очень много чего исправлять пришлось, по сути обещанный ActionScript подобный синтаксис я не особо получил.

Но и самое главное, что в итоге, когда проект собрался, то игра не заработала, она не заработала ни под одну платформу. Вывод графики напоминал то, что программа неправильно обрабатывает шейдеры. Хотя под некоторые платформы был какой-то проблеск того, что вроде какая-то там графика пытается нарисоваться, но например под HTML5 вообще пусто, чёрный экран. А я то ведь уже замечтался, что у меня в руках хороший инструмент, который почти Flash, и с помощью него я смогу делать игры и под Windows и под HTML5, используя по сути всё тот же Flash. В общем я проверил отрисовку через GPU, правильные ли остались шейдеры от прежней программы, потратил на это несколько часов. В итоге пришёл к выводу, что эта OpenFL очень сырая, отрисовка через GPU содержит много ошибок и не повторяет того как работает Flash.

Но может быть Haxe применим в чём-то другом? Я попробовал поискать вакансии на hh.ru с ключевым словом Haxe, но он нашёл ровно 0 результатов. Мой вывод: Haxe и библиотеки под него работают достаточно криво, сфера использования этого языка очень ограничена (если вообще существует), изучать и копаться в нём нет смысла, так как он никому не нужен, а для самостоятельного использования не даёт нужного результата.

Теги , ,
Автор

Опубликовано
Комментарии 0

Splinter Jump – вторая игра моего авторства. Вдохновлённый игрой Doodle Jump, а она была очень популярная почти 8 лет назад, я решил сделать свою игру с похожей механикой. Кроме паскаля я тогда ничего не знал, соответственно, и эта игра была написана на паскале, в среде PascalABC.NET. Суть игры заключается в том, чтобы прыгать по платформам и добраться до верха и найти телевизор. На уровне разбросаны сыр и напитки. Сыр нужно собирать для зарабатывания очков, напитки дают улучшенный прыжок на ограниченное время. Игра полностью завершённая, в ней реализованы все задуманные уровни, в игре 10 уровней, в некоторых уровнях присутствуют боссы. Персонаж игры может атаковать, соответственно механика не ограничена только прыжками по платформам.

Через пару лет, когда изучил flash, то я решил переписать эту же игру на нём и загрузить её во ВК. Но к сожалению, ВК больше не поддерживают flash игры и они отключили мою игру. Первые четыре скрина – версия для windows, написанная на pascal, последние четыре скрина – flash версия для ВК, написанная в среде FlashDevelop, как видно по скрину была добавлена возможность поставить игру на паузу.

Исходный код pascal версии: https://bitbucket.org/Eg0r/splinter-jump




Теги , , , ,
Автор

Опубликовано
Комментарии 2

Так как я в процессе программирования больше всего ценю сложность задач, то решил ознакомиться с программированием микроконтроллеров. Считаю, что программирование мк довольно не тривиальная задача и есть над чем поразмыслить. Да и не всю же жизнь знать только одну лишь Arduino. Начинаем вылезать из ардуиновских штанов. Я хотел начать с stm32, но всё-таки решил, что хочу попробовать PIC для начала. Скачал MPLAB IDE X, хочу сказать, что это очень удобная среда разработки, тем более после Arduino IDE уж точно. Далее устанавливаю компилятор XC8, вот тут я немного подгорел, что компилятор не включён в среду разработки, или же хотя бы нет скачки и установки прям через саму MPLAB IDE. Но не страшно, установка достаточно простая.

Далее я создаю проект, выбираю контроллер PIC18F4550, выбор обусловлен тем, что пока я смотрел в гугле информацию по PIC, то часто попадались примеры и туториалы именно по этой модели контроллера. Настало время поставить перед собой задачу и решить её. На мой взгляд очень интересная и полезная задача (возможно потом использую в дальнейших проектах) - это реализация некоторого протокола для связи с ПК по COM порту. То есть в программе нужно реализовать связь по UART в рамках некоторого протокола.

Протокол, который я буду реализовывать: Первый байт пакета - длина пакета. Второй байт пакета - номер пакета. Третий байт - команда. Остальные байты - какие-то данные. Последние два байта - контрольная сумма пакета, буду использовать CRC16. Пакеты должны следовать строго по нумерации, нумерацию будем считать одним байтом, после 0го пакета должен следовать 1ый, 1го - 2ой ... 254го - 255ый, 255го - 0ой. Пакеты, которые ПК пришлёт вне нумерации будут отброшены. Пакеты с неверной контрольной суммой тоже будут отброшены. Контроллер не подтверждает приём каждого пакета, заместо этого он периодически шлёт пакет, в котором содержится информация с номером последнего принятого пакета.

А теперь время приступить к написанию кода. Начать я решил с того, чтобы разобраться как работать с UART в своей программе. Оказалось, что есть два варианта: через библиотеки или напрямую работая с определёнными регистрами. Библиотечные методы довольно просты, я решил ознакомиться с ними, но потребовалось дополнительно установить библиотеку "Peripheral Library", но вот сколько я не мучался настроить работать компилятор XC8 с ней - мне не удалось. Было решено напрямую управлять регистрами из программы для работы с UART. Реализовал по примеру из инета, теперь нужно как-то это всё проверить.

Так как у меня нет желания возиться с реальным железом, вернее мне просто хочется программировать здесь и сейчас, не отвлекаясь на что-либо другое, то однозначно нужно как-то симулировать работу контроллера и посмотреть как работает на нём программа. В процессе я познакомился с двумя способами симулировать работу мк на компьютере.

Первый: есть замечательная программа PIC18 Simulator IDE от разработчика OshonSoft, она позволяет симулировать работу мк PIC 18-ой серии. К слову, на сайте присутствуют и симуляторы под PIC других серий, а также симулятор AVR. Также у них есть программа для мониторинга последовательного порта, отличная штука, до этого мне попадались мониторы COM порта отвратительного качества, с глюками, либо же платные, которые и попадаются в гугле. Так что Serial Port Monitor от OshonSoft очень годная штука и дальше ещё может пригодиться.

Работать в данном симуляторе достаточно просто, выбираем частоту, нужную модель контроллера, загружаем прошивку в виде .hex файла, выбираем нужный темп (скорость) симуляции, стартуем симуляцию. Программа имеет широкий спектр инструментов, которые представлены на скрине. Нам же сейчас нужен инструмент для проверки работоспособности UART. Для этого есть Hardware UART Simulation Interface и Software UART Simulation Interface. В Hardware interface всё заработало отлично, то есть видно что приходят байты нам в качестве ответа. А вот Software interface у меня так и не завёлся, к слову я считаю что это какой-то баг. В любом случае, эта программа не позволяет работать с мк через виртуальный COM порт, поэтому не смотря на многочисленные достоинства этой программы, она на данный момент не подходит.

Второй способ: симуляция работы мк в Proteus. Этот способ и придётся использовать, так как для работы с мк хочется иметь в системе COM порт, по которому можно послать данные контроллеру, или получить данные от контроллера, а Proteus это точно умеет. С Proteus я особо никогда не работал, поэтому не особо хотелось с ним знакомиться, но надо, так надо. Далее расскажу как создать виртуальный COM порт в системе и соединиться через него с симулированным мк в Proteus.

Кидаем на рабочую область нужный нам микроконтроллер, кидаем компонент COMPIM, соединяем TX с TX, RX с RX. Указываем в настройках микроконтроллера в Proteus нужную частоту и .hex файл. Далее нам нужно создать и соединить между собой два виртуальный последовательных порта. С этим нам поможет программа com0com. В ней ничего сложного, там уже есть созданные порты, выглядеть должно как на скрине. Теперь один COM порт прописываем в настройках COMPIM в Proteus, а другой можно использовать либо в своей собственной программе либо в уже готовой терминальной программе (например Putty), главное выставить верный baudrate.

Но тут не всё так просто. Потому что моя программа для PIC наотрез отказывалась верно работать в Proteus. Сначала я винил скрытые ошибки в моём коде, поэтому раз за разом пробовал всё новые примеры кода для работы с UART из гугла. Когда уже стало понятно, что в коде ошибки точно нет, то закралась мысль, что Proteus при настройке тактовой частоты в 4МГц и baudrate в 9600 что-то не так моделирует, может что-то не успевает и где-то обсчитывается. Я понизил тактовую частоту до 1МГц и baudrate до 300 - и заработало! Какое-то время я винил косяки в симуляции Proteus, но на форуме добрые люди подсказали, что при моих изначальных параметрах ошибка UART достигала 6.99% и отсюда следовали все проблемы. По даташиту выбрал другие параметры, чтобы и 9600 и 4МГц, и в итоге прошивка отлично заработала! Так как во всех туториалах, которые я прочитал по UART на PIC не было сказано про эту проблему ни слова, а промучался я с этим несколько часов, то хочу этим текстом обратить внимание на это новичков. На одном скрине параметры, которые портят работу с UART, на другом уже с ошибкой всего 0.16%, с которой можно хорошо жить.

А вот код программы:

#define _XTAL_FREQ 4000000

#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

#include "fastcrc.h"
#include "commands.h"

struct Packet
{
    uint8_t length;
    uint_least8_t data[100];
};

uint8_t lastNumPacket;

void initUART(void);
void writeUART(unsigned char data);

bool dataAvailable(void);
bool overrunError(void);

void analyzePacket(struct Packet* packet);
bool checkCheckSum(struct Packet* packet);
bool checkCorrectNumPacket(uint8_t numPacket);

void main(void) 
{     
    initUART();

    uint8_t counterBytes = 0;
    lastNumPacket = 255;
    struct Packet packet;

    while (1)
    {
        if (dataAvailable() == true)
        {
            if (overrunError() == true)
            {           
                CREN = 0;
                NOP();
                CREN = 1;
            }
            if (counterBytes == 0)
            {
                packet.data[0] = RCREG;
                packet.length = packet.data[0];
                counterBytes++;
            }
            else 
            {
                packet.data[counterBytes] = RCREG;
                counterBytes++;
                if (counterBytes == packet.length)
                {
                    counterBytes = 0;
                    if (checkCorrectNumPacket(packet.data[1]) == true && checkCheckSum(&packet) == true)
                    {
                        lastNumPacket++;
                        writeUART(0x33);
                        analyzePacket(&packet);
                    }
                }
            }
        }
    }
    return;
}

void initUART()
{
    TXSTA = 0; //Reset USART registers to POR state
    RCSTA = 0;

    TRISC6 = 0; //TX as output
    TRISC7 = 1; //RX as input
    SYNC = 0; //Async operation
    TX9 = 0;  //No tx of 9th bit
    RX9 = 0;  //No rx of 9th bit
    TXEN = 1;  //Enable transmitter
    CREN = 1; //Enable receiver
    SPEN = 1; //Enable serial port

    TXIE=0; //disable tx interrupts
    RCIE=0; //disbale rx interrupts 

    BRG16 = 0; //Divisor at 8 bit
    BRGH = 1; //No high-speed baudrate
    SPBRG = 25;  //9600 baudrate

    RCIF = 0;
}

void writeUART(unsigned char data)
{
  while(!TRMT);
  TXREG = data;
}

bool dataAvailable()
{
    if (RCIF != 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool overrunError()
{
    if (RCSTAbits.OERR)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void analyzePacket(struct Packet* packet)
{
    uint8_t command = packet->data[2];
    switch (command)
    {
        case COMMAND_1: break;
    }
}

bool checkCorrectNumPacket(uint8_t numPacket)
{
    uint8_t ifReceivedLastNumPacket = lastNumPacket + 1;
    if (ifReceivedLastNumPacket == numPacket)
    { 
        return true;   
    }    
    else
    {
        return false;
    }
}

bool checkCheckSum(struct Packet* packet)
{
    uint16_t packetCrc = *((uint16_t*)(&packet->data + packet->length - 2));
    uint16_t calculatedCrc = fast_crc_blk(&(packet->data), packet->length - 2);
    if (calculatedCrc == packetCrc)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Логика кода такая: инициализируем UART, далее в бесконечном цикле ждём байты, если принят байт, то считаем его за длину пакета, отсчитываем длину, далее проверяем соответствие нумерации и контрольную сумму (реализацию расчёта контрольной суммы я приводить тут не стал, так как тема статьи это не алгоритмы подсчёта контрольных сумм), если проверка прошла успешно, то принимаем пакет и выполняем команду из этого пакета.

Я намеренно реализовал без прерываний, руководствуясь тем, что если бы использовались прерывания, то при большом количестве пакетов программа бы застряла бы в этих прерываниях обработки UART и совсем забыла бы про основную работу, которую должен выполнять контроллер. Поэтому на мой взгляд логично обрабатывать UART без прерываний.

Теги , ,
Автор

| 1 | 2 | 3 | 4 | 5 | | 8 |