{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Блоги: заметки с тегом sectorc",
    "_rss_description": "Автоматически собираемая лента заметок, написанных в блогах на Эгее",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": false,
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/blogengine.me\/blogs\/tags\/sectorc\/",
    "feed_url": "https:\/\/blogengine.me\/blogs\/tags\/sectorc\/json\/",
    "icon": false,
    "authors": [
        {
            "name": "Илья Бирман",
            "url": "https:\/\/blogengine.me\/blogs\/",
            "avatar": false
        }
    ],
    "items": [
        {
            "id": "120944",
            "url": "https:\/\/bolknote.ru\/all\/eschyo-o-pechati-strok-v-sectorc\/",
            "title": "Ещё о печати строк в SectorC (лонгрид)",
            "content_html": "<p>Недавно познакомился с интересным компилятором подмножества языка Си — <i>SectorC<\/i>. Он интересен тем, что влезает в один сектор (512 байт), но позволяет писать вполне реальные программы. В реализованном языке очень много нет, тем не менее, это очень интересное достижение и мне было интересно поразбираться как всё сделано. Кстати, результат автора мне удалось в несколько приёмов уменьшить на несколько байт.<\/p>\n<p>Из-за ограниченного синтаксиса в язык ни в каком качестве не влезли строки, хотя в стандартной библиотеке языка есть способ вывести символ по его коду. Я уже <a href=\"https:\/\/bolknote.ru\/all\/svyortka-strok-na-sectorc\/\">делал попытку<\/a> как-то упростить себе жизнь, когда писал «Песню о пиве» и мне понадобилось выводить последовательность символов, но почти сразу после этого мне пришла в голову идея получше.<\/p>\n<p>Для того, чтобы рассказать в чём смысл, надо для начала немного углубиться в то как работает этот компилятор.<\/p>\n<p>Компилятор, когда разбирает код, делит его на токены по пробелу, после каждый токен хешируется функцией <tt>atoi<\/tt> в двухбайтовое число и уже по нему определяется является ли встреченное ключевым словом языка, либо именем функции или переменной.<\/p>\n<p>В последнем случае, имя, преобразованное в число, будет в машинных кодах использовано как смещение, указывающее на ячейку со значением переменной или с указателем на тело функции.<\/p>\n<p>Отсюда следует, что последовательно называющиеся переменные — <tt>a<\/tt>, <tt>b<\/tt>, <tt>c<\/tt> и так далее, с будут в памяти располагаться последовательно со смещением в два байта, точностью до переполнения. При этом их даже не обязательно определять — переменная с любым именем существует с начальным нулевым значением.<\/p>\n<p>Идём дальше.<\/p>\n<p>В языке есть любопытная операция — <tt>&<\/tt>, позволяющая получить адрес переменной, который, напомню, равен одновременно хешу (<tt>atoi<\/tt>) к её имени. Для однобуквенных имён это позволяет по полученному адресу восстановить код символа — надо поделить полученное значение на два и добавить 48 — код символа <tt>0<\/tt>.<\/p>\n<p>Таким образом, передав в функцию адреса <tt>& H<\/tt>, <tt>& e<\/tt>, <tt>& l<\/tt>, <tt>& l<\/tt>, <tt>& o<\/tt>, мы сможем восстановить из них символы и составить из них слово <tt>Hello<\/tt>.  Если записать эти адреса в переменные, которые располагаются в памяти подряд, можно, читая память последовательно, выводить закодированную строку.<\/p>\n<p>Есть ли в языке способ прочитать что-то по адресу? Как ни странно, есть — токен <tt>*(int*)<\/tt> позволяющий это сделать.<\/p>\n<p>Теперь всё вышесказанное можно соединить в следующий довольно компактный код:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">void print_str()\r\n{\r\n    print_str_pointer = &amp; _0;\r\n    while( *(int*) print_str_pointer ){\r\n        print_ch = ( *(int*) print_str_pointer &gt;&gt; 1 ) + 48; print_char();\r\n        print_str_pointer = print_str_pointer + 2;\r\n    }\r\n}\r\n\r\nvoid main()\r\n{\r\n    _0 = &amp; H; _1 = &amp; e; _2 = &amp; l; _3 = &amp; l; _4 = &amp; o; _5 = 0;\r\n    print_str(); \/\/ Hello\r\n}<\/code><\/pre>",
            "date_published": "2023-06-24T21:49:21+05:00",
            "date_modified": "2023-06-24T21:49:18+05:00",
            "tags": [
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 24 Jun 2023 21:49:21 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120944",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120446",
            "url": "https:\/\/bolknote.ru\/all\/return-v-sectorc\/",
            "title": "return в SectorC",
            "content_html": "<p>Вчера перед сном пришло в голову, что один из недостатков языка <i>SectorC<\/i>, — отсутствие конструкции <tt>return<\/tt>, можно отчасти компенсировать средствами самого языка. Достаточно написать функцию <tt>return()<\/tt>, которая будет делать две вещи — возвращать значение (об этом ниже) и прерывать выполнение функции из которой её вызвали.<\/p>\n<p>Я уже достаточно знаю об его внутренностях, чтобы понимать как это можно сделать.<\/p>\n<p>Второе реализовать совсем просто, тут мне помогает некогда богатый опыт программирования на Ассемблерах. <i>SectorC<\/i> транслируется напрямую в машинный код, а значит адреса возвратов из функций хранятся на стеке. Так что надо всего лишь удалить верхний адрес возврата и мы вернёмся не в функцию, которая нас вызвала, а на уровень выше. Я делаю это командой <tt>pop ax<\/tt>.<\/p>\n<p>Теперь посмотрим на первую задачу.<\/p>\n<p>Как я уже <a href=\"https:\/\/bolknote.ru\/all\/div-in-sectorc\/\">писал<\/a> язык устроен так, что присваивание переменных друг другу происходит через регистр <tt>AX<\/tt>, таким образом, присваивая переменную самой себе, мы помещаем её в этот регистр. Этот факт используется в стандартной библиотеке языка, так что можно считать, что это стабильное поведение.<\/p>\n<p>Напрашивается следующая реализация, — перед вызовом присваиваем возвращаемую переменную самой себе, в функции <tt>return()<\/tt> записываем содержимое <tt>AX<\/tt> в какую-нибудь переменную (я выбрал <tt>return<\/tt>) и возвращаемся.<\/p>\n<p>Моя реализация вместе с примером использования ниже:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">void return()\r\n{\r\n    \/\/ mov [&amp;return, ax]\r\n    asm 163; asm 236; asm 229;\r\n    \/\/ pop ax (clear stack from prev return address)\r\n    asm 88;\r\n}\r\n\r\nvoid random()\r\n{\r\n    \/\/ xor ax, ax    int 0x1A         xchg ax, dx\r\n    asm 49; asm 192; asm 205; asm 26; asm 146;\r\n    \/\/ xor dx, dx    mov cx, 10\r\n    asm 49; asm 210; asm 185; asm 10; asm 0;\r\n    \/\/ div cx         inc dx  xchg ax, dx\r\n    asm 247; asm 241; asm 66; asm 146;\r\n    return();\r\n}\r\n\r\nvoid function()\r\n{\r\n    i = 0; while( i &lt; 20 ){\r\n        random();\r\n        if( i &gt;  return ){\r\n            i = i; return();\r\n        }\r\n        i = i + 1;\r\n    }\r\n\r\n    i = 0; return();\r\n}\r\n\r\nvoid main()\r\n{\r\n    function();\r\n    print_num = return; print_u16();\r\n}<\/code><\/pre><p>Кстати, возникает любопытный сторонний эффект — так как через <tt>AX<\/tt> делается не только присваивание, а вообще все операции, то функция <tt>return()<\/tt> захватывает любое последнее вычисленное выражение, что тоже может быть удобно.<\/p>\n",
            "date_published": "2023-06-17T08:37:44+05:00",
            "date_modified": "2023-06-17T08:36:48+05:00",
            "tags": [
                "sectorc",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 17 Jun 2023 08:37:44 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120446",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120432",
            "url": "https:\/\/bolknote.ru\/all\/div-in-sectorc\/",
            "title": "Деление в SectorC",
            "content_html": "<p>Очень неудобно, что в языке <i>SectorC<\/i> нет деления. Я сначала думал, что это из-за ограничений — ведь задумка в том, чтобы компилятор помещался в сектор (512 байт). В таком случае можно было бы выкинуть операции левого и правого сдвига (<tt>&lt;&lt;<\/tt> и <tt>&gt;&gt;<\/tt>), чтобы освободить место. Эти операции как раз можно было бы заменить умножением и делением.<\/p>\n<p>Но сегодня для интереса я попытался добавить в код новый токен для деления и оказалось, что места хватает. Закинул автору пулл-реквест, посмотрим примет ли. После моих изменений там осталось ещё шестнадцать байт, можно и ещё язык расширить.<\/p>\n<p>Впрочем, <i>SectorC<\/i> устроен так, что в программе можно использовать машинные коды. Можно написать деление прямо в них, если воспользоваться <a href=\"https:\/\/shell-storm.org\/online\/Online-Assembler-and-Disassembler\">ассемблером<\/a>.<\/p>\n<p>При этом надо знать две вещи: присваивание значения переменной происходит через регистр <tt>AX<\/tt>, так что присвоив переменную саму себе, можно записать в <tt>AX<\/tt> её значение, и второе — если посчитать <tt>atoi<\/tt> (см. <a href=\"https:\/\/bolknote.ru\/all\/sectorcfuck\/\">мою вчерашнюю заметку<\/a>) для имени переменной и умножить получившееся число на два, можно получить ячейку, где хранится значение этой переменной.<\/p>\n<p>Так вычисленный результат можно будет положить куда вздумается.<\/p>\n<p>Объединив всё вышесказанное, получим такой результат:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">int div_a; int div_b; \/\/ input\r\nint div_result;\r\n\r\nvoid div()\r\n{\r\n    div_b = div_b;             \/\/ mov ax, [&amp;div_b]\r\n    asm 145;                   \/\/ xchg cx, ax\r\n    asm 49; asm 210;           \/\/ xor dx, dx\r\n    div_a = div_a;             \/\/ mov ax, [&amp;div_a]\r\n    asm 247; asm 241;          \/\/ div cx\r\n\r\n    asm 163; asm 48; asm 193;  \/\/ mov [&amp;div_result], ax\r\n}\r\n\r\nvoid main()\r\n{\r\n    div_a = 35500; div_b = 113; div();\r\n    print_num = div_result; print_u16(); \/\/ «314»\r\n}<\/code><\/pre>",
            "date_published": "2023-06-16T00:10:28+05:00",
            "date_modified": "2023-06-17T08:07:35+05:00",
            "tags": [
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Fri, 16 Jun 2023 00:10:28 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120432",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120405",
            "url": "https:\/\/bolknote.ru\/all\/sectorcfuck\/",
            "title": "SectorCFuck",
            "content_html": "<p>Очень интересно устроен разбор файла программы в <a href=\"https:\/\/web.archive.org\/web\/20230609141452\/https:\/\/habr.com\/ru\/companies\/ruvds\/articles\/740310\/\">компиляторе <i>SectorC<\/i><\/a>, который я ковыряю вечерами из любви к ненормальному программированию. Для тех, кто успел позабыть, напомню — этот компилятор занимает один сектор (512 байт) и способен выполнять программу на подмножестве Си.<\/p>\n<p>Когда смотришь на код такого маленького объёма, сразу возникает вопрос — каким чудом удалось туда запихнуть грамматику языка Си, даже если какое-то подмножество? Ответ — хеширование.<\/p>\n<p>Автор использует реализацию функции <tt>atoi<\/tt>, которая превращает любые строки в шестнадцатибитное числа:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">unsigned short sectorc_atoi(const char *s)\r\n{\r\n    unsigned short n = 0;\r\n    for (;;) {\r\n        char c = *s++;\r\n        if (!c) break;\r\n\r\n        n = 10 * n + (c - &#039;0&#039;);\r\n    }\r\n\r\n    return n;\r\n}<\/code><\/pre><p>Все токены, которые встречаются в программе, обязательно разделяются пробелами (за исключением <tt>;<\/tt>, для него есть специальная обработка). Это позволяет довольно просто парсить программу — любой токен скармливаем <tt>atoi<\/tt>, получаем число и по таблице смотрим с чем имеем дело.<\/p>\n<p>Если в таблице число не находится, значит это переменная, её численное значение, умноженное на два, даёт двухбайтовую область памяти, где надо взять значение. Для чисел, видимо, есть какая-то отдельная логика, я не читал подробно исходник, но из моих экспериментов как будто бы следует, что числом считается всё, что не токен и не может быть именем переменной.<\/p>\n<p>Перед запуском программа проходит через линтер, который не является частью компилятора. Он нужен для поиска ошибок в коде и коллизий в получившихся хешированных значениях. В последнем случае линтер останавливается с ошибкой, показывая хеши каких токенов совпали между собой.<\/p>\n<p>Если линтер выключить, можно достигнуть интересного эффекта. Для каждого токена можно вычислить коллизию позаковыристей и написать всю программу без букв и цифр. <i>SectorC<\/i> как будто бы «из коробки» предназначен для запуска обфусцированного кода.<\/p>\n<p>Ниже программа, выводящая «<i>Hello<\/i>» (её надо запускать <i>без линтера<\/i>):<\/p>\n<pre class=\"e2-text-code\"><code class=\"plaintext\">&lt;**^ &#039;)&#039;|] \/=]\r\n    -_.@&quot; -[ )~( \/@&lt; $^+&gt;\\() \/&lt;&#039;\r\n    -_.@&quot; -[ \/&lt;&#039;&#039; \/@&lt; $^+&gt;\\() \/&lt;&#039;\r\n    -_.@&quot; -[ ,[~ \/@&lt; $^+&gt;\\() \/&lt;&#039;\r\n    -_.@&quot; -[ +^^` \/@&lt; $^+&gt;\\() \/&lt;&#039;\r\n    -_.@&quot; -[ \/&lt;(&#039; \/@&lt; $^+&gt;\\() \/&lt;&#039;\r\n,[_<\/code><\/pre><p>Я поленился и не стал рассчитывать разные значения для одинаковых токенов, которые упоминаются несколько раз, но можно сделать и это, тогда восстановление исходного текста будет безумно затратной задачей.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2023.06.14@2x.jpg\" width=\"1000\" height=\"128\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Пример запуска обфусцированной программы без линтера<\/div>\n<\/div>\n<p>Обфусцированный таким образом листинг, запускающий файл и программу для поиска коллизий <a href=\"https:\/\/github.com\/bolknote\/SectorC-obstruction\">выложил на «Гитхаб»<\/a>, можно посмотреть подробности там.<\/p>\n<p>Кстати, такое развлечение, когда мы не используем в программе алфавитно-цифровые символы, называется <a href=\"https:\/\/bolknote.ru\/tags\/smthfuck\/\">ЧтоНибудь<i>Fuck<\/i><\/a>, уж так повелось, — <i>FuckJS<\/i>, <i>FuckPHP<\/i> и так далее. Отсюда и название заметки.<\/p>\n",
            "date_published": "2023-06-14T20:08:51+05:00",
            "date_modified": "2023-06-15T23:54:51+05:00",
            "tags": [
                "*fuck",
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 14 Jun 2023 20:08:51 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120405",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120363",
            "url": "https:\/\/bolknote.ru\/all\/svyortka-strok-na-sectorc\/",
            "title": "Свёртка строк на SectorC (лонгрид)",
            "content_html": "<p>Вчера я <a href=\"https:\/\/bolknote.ru\/all\/99-butylok-sectorc\/\">обещал<\/a> рассказать что я придумал, чтобы компактнее выводить строки в языке <i>SectorC<\/i>. Напомню, это компактный компилятор подмножества Си, который занимает один сектор (512 байт).<\/p>\n<p>Для начала посмотрим что доступно в компиляторе для вывода текста. В самом языке для этого ничего нет, но если заглянуть в стандартную библиотеку языка, которая подключается автоматически, то можно увидеть там функции <tt>print_char()<\/tt>, <tt>print_u16()<\/tt> и <tt>print_i16()<\/tt>, предназначенные, соответственно, для вывода символа, заданного его кодом, целого числа без знака и целого числа со знаком.<\/p>\n<p>Соответственно вывод «<i>Hello<\/i>» выглядит так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">print_ch =  72; print_char(); \/\/ H\r\nprint_ch = 101; print_char(); \/\/ e\r\nprint_ch = 108; print_char(); \/\/ l\r\nprint_ch = 108; print_char(); \/\/ l\r\nprint_ch = 111; print_char(); \/\/ o<\/code><\/pre><p>Очень многословно. Когда я вчера написал «песню о пиве» в таком формате, программа получилась чересчур длинной. Мне захотелось как-то её упросить. Вот как я рассуждал.<\/p>\n<p>Код задаётся целым типом <tt>int<\/tt>, который имеет в этом языке 65536 значений. Поскольку мне нужны не все буквы, выглядит так как будто в одно число можно запихнуть несколько знаков. Но сколько?<\/p>\n<p>Для начала посмотрим сколько символов вообще используется. Цифры меня не интересуют (числа выводятся отдельной функцией), поэтому выкинем их:<\/p>\n<pre class=\"e2-text-code\"><code class=\"python\">import sys\r\nfrom collections import Counter\r\ndata = Counter(x for x in &#039;&#039;.join(sys.stdin.readlines()) if x &gt; &#039;9&#039; or x &lt; &#039;0&#039;)\r\n\r\nprint(data)<\/code><\/pre><p>Вот что получилось:<\/p>\n<pre class=\"e2-text-code\"><code class=\"python\">Counter({&#039; &#039;: 2306, &#039;e&#039;: 1305, &#039;o&#039;: 1108, &#039;t&#039;: 903, &#039;l&#039;: 702, &#039;b&#039;: 601, &#039;n&#039;: 600, &#039;a&#039;: 598, &#039;s&#039;: 497, &#039;r&#039;: 403, &#039;f&#039;: 300, &#039;w&#039;: 300, &#039;\\n&#039;: 298, &#039;d&#039;: 298, &#039;h&#039;: 202, &#039;,&#039;: 200, &#039;.&#039;: 200, &#039;u&#039;: 100, &#039;T&#039;: 99, &#039;i&#039;: 99, &#039;k&#039;: 99, &#039;p&#039;: 99, &#039;m&#039;: 4, &#039;G&#039;: 1, &#039;N&#039;: 1, &#039;y&#039;: 1})<\/code><\/pre><p>Двадцать шесть символов. Жаль не шестнадцать, было бы удобно хранить — как я говорил выше, значений в целом типе помещается 65536 штук. Какая связь? Чтобы это понять, разберёмся откуда взялось число 65536.<\/p>\n<p>Оно вытекает из особенностей машинного хранения. Минимальная единица хранения — бит, хранит всего два состояния, — биты, в свою очередь, собраны по восемь штук в байты. Тип <tt>int<\/tt> в данном случае — это два байта.<\/p>\n<p>Отсюда получается, байт — восемь бит по два значения или 2⁸ = 256 значений, два байта — 256² = 65536. В десятичной системе выглядит довольно бессмысленно, но если перейти на шестнадцатеричную, где цифры записываются от 0…9, A…F, с префиксом <tt>0x<\/tt> (он ставится чтобы различить две формы записи числа), то становится понятнее.<\/p>\n<p>Тогда число 65536 будет выглядеть как <tt>0xFFFF<\/tt> — визуально распадается на два байта по 256 (<tt>0xFF<\/tt>) значений или на четыре полубайта по 16 (<tt>0xF<\/tt>).<\/p>\n<p>Я решил хранить символы в полубайтах. Поскольку символов у меня 26, а в полубайте помещается 16 значений, то одно значение пришлось занять префиксом — если он встречается, то мы используем вторую таблицу символов. Под префикс я выделил <tt>0xF<\/tt>. Таким образом, в одном <tt>int<\/tt> можно закодировать от двух до четырёх символов.<\/p>\n<p>Например, число <tt>0x2F40<\/tt> или <tt>0x2<\/tt> <tt>0xF<\/tt> <tt>0x4<\/tt> <tt>0x0<\/tt> означает, что мы смотрим второй символ в первой таблице, потом переключаемся на вторую (так как встретили <tt>0xF<\/tt>) и смотрим там четвёртый символ, а символ <tt>0x0<\/tt> имеет специальное значение, — он не выводится и нужен для того, чтобы у нас была возможность закодировать строку, которая короче, чем могла бы поместиться.<\/p>\n<p>В первую таблицу я поместил самые часто встречающиеся символы, во вторую — те, что встречаются реже. Получилось намного компактнее. Например, строка «<i>hello<\/i>» в моём алфавите выводится вот так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">s = 21023; ps(); s = 53; ps(); \/\/ 0x521F, 0x35<\/code><\/pre><p>Поскольку по техническим причинам число проще обрабатывать с конца, то я разворачиваю ту часть строки, которая кодируется. Это легко заметить по тому, что префиксное значение <tt>0xF<\/tt> у меня находится в конце числа <tt>0x521F<\/tt>.<\/p>\n<p>Код для преобразования строк в такую форму выглядит следующим образом:<\/p>\n<pre class=\"e2-text-code\"><code class=\"python\">input = &#039;hello&#039;\r\n\r\nfrom itertools import *\r\n# основная таблица\r\nt0 = [&#039; &#039;, &#039;e&#039;, &#039;o&#039;, &#039;t&#039;, &#039;l&#039;, &#039;b&#039;, &#039;n&#039;, &#039;a&#039;, &#039;s&#039;, &#039;r&#039;, &#039;f&#039;, &#039;w&#039;, &#039;\\n&#039;, &#039;d&#039;]\r\n# и дополнительная\r\nt1 = [&#039;h&#039;, &#039;,&#039;, &#039;.&#039;, &#039;u&#039;, &#039;T&#039;, &#039;i&#039;, &#039;k&#039;, &#039;p&#039;, &#039;m&#039;, &#039;G&#039;, &#039;N&#039;, &#039;y&#039;]\r\n\r\n# поиск символов в таблицах\r\ndef conv(ch):\r\n    try:\r\n        yield t0.index(ch) + 1\r\n    except ValueError:\r\n        yield 0xF\r\n        yield t1.index(ch) + 1\r\n\r\n# подготовка к разбиению по группам, которые будут кодироваться вместе\r\ndef make_shifter():\r\n    pos = 0\r\n    gr  = 0\r\n\r\n    def shifter(v):\r\n        nonlocal pos\r\n        nonlocal gr\r\n\r\n        if pos == 4:\r\n            pos = 0\r\n            gr += 1\r\n\r\n        # префикс переключения таблиц не может остаться в одиночестве,\r\n        # поэтому мы не можем оставить его в хвосте\r\n        if pos == 3 and v == 0xF:\r\n            pos = 1\r\n            gr += 1\r\n        else:\r\n            pos += 1\r\n\r\n        return (v, gr, )\r\n    return shifter\r\n\r\nshifter = make_shifter()\r\n\r\n# нумеруем группы символов\r\ngen = (shifter(x) for x in chain(*(conv(ch) for ch in input)))\r\n# собираем группы символов вместе\r\ngen = groupby(gen, key = lambda x: x[1])\r\n\r\n# двигаемся по собранным группам, собираем их в число\r\nfor x in gen:\r\n    sum = 0\r\n    for y in list(x[1])[::-1]:\r\n        sum *= 16\r\n        sum += y[0]\r\n\r\n    print(&quot;s = {}; ps(); &quot;.format(sum))<\/code><\/pre><p>Теперь только осталось запрограммировать декодирующую часть в <i>SectorC<\/i>. У нас будет две функции — одна достаёт из числа по одном полубайту за раз, вторая преобразовывает полубайт в соответствующий символ. Как всё работает в деталях объяснять не буду, покажу с минимальными комментариями.<\/p>\n<p>Первая выглядит совсем просто:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">int s; \/\/ input\r\nvoid ps() {\r\n    while( s ){\r\n        c = s &amp; 15;\r\n        c2c();\r\n        s = s &gt;&gt; 4;\r\n        if( s == ( 0 - 1 ) ){ s = 0; } \/\/ signed workaround\r\n    }\r\n}<\/code><\/pre><p>Напоминаю, что вы языке у функции нет аргументов и возможности вернуть значение — всё делается через глобальные переменные, поэтому код выглядит странновато.<\/p>\n<p>Строка с комментарием нужна, так как число у нас знаковое (то есть возможные 65536 значений делятся пополам, часть значений считается отрицательным, другая часть — положительными). У отрицательных чисел есть особенность — битовый сдвиг (<tt>&gt;&gt;<\/tt>) хоть и считается эквивалентом деления на два, но из минус единицы ноль не делает.<\/p>\n<p><tt>c2c<\/tt> тут — как раз функция, которая должна преобразовывать код в символ (<i>code-to-character<\/i>). Она выглядит сложнее. Поскольку массивов в языке тоже нет, приходится выкручиваться как-то иначе. Самый простой способ — инструкциями <tt>if<\/tt> проверять каждое значение и найдя нужно, записать в специальную переменную искомый код.<\/p>\n<p>Сначала я так и сделал, но выглядело, опять же, громоздко. В итоге, переделал чуть попроще: код сравнения выделил в короткую функцию <tt>x()<\/tt>, а пару что во что превращается закодировал в один <tt>int<\/tt>:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">int c; \/\/ input\r\nint x;\r\nvoid x() { if( c == ( x &gt;&gt; 8 ) ){ print_ch = x &amp; 255; } }<\/code><\/pre><p>После такого кодирования функция <i>code-to-character<\/i> стала выглядеть вот так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">void c2c() {\r\n    c = c + shift;\r\n    x =  288; x(); x =  613; x(); x =  879; x(); x = 1140; x(); x = 1388; x();\r\n    x = 1634; x(); x = 1902; x(); x = 2145; x(); x = 2419; x(); x = 2674; x();\r\n    x = 2918; x(); x = 3191; x(); x = 3338; x(); x = 3684; x(); x = 4200; x();\r\n    x = 4396; x(); x = 4654; x(); x = 4981; x(); x = 5204; x(); x = 5481; x();\r\n    x = 5739; x(); x = 6000; x(); x = 6253; x(); x = 6471; x(); x = 6734; x();\r\n    x = 7033; x();\r\n\r\n    if( c != 15 ){ print_char(); }\r\n    shift = 0; if( c == 15 ){ shift = 15; }\r\n}<\/code><\/pre><p>Как видите, код <tt>0xF<\/tt> (15) устанавливает флаг сдвига значений <tt>shift<\/tt>, который в следующий раз складывается с кодом (<tt>c + shift<\/tt>) и сдвигает значение на следующем декодировании. Заметно, что нигде нет <tt>else<\/tt>, потому что этого оператора в языке тоже нет!<\/p>\n<p>Как будто бы рассказал всё.<\/p>\n<p>Вместо вывода — интересный своей компактностью компилятор, автору моё уважение, — я даже и не подозревал, что в таком малом объёме можно уложить полноценный язык! Очень и очень круто!<\/p>\n",
            "date_published": "2023-06-12T12:10:13+05:00",
            "date_modified": "2023-06-16T17:15:01+05:00",
            "tags": [
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Mon, 12 Jun 2023 12:10:13 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120363",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120360",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-sectorc\/",
            "title": "99 бутылок: SectorC",
            "content_html": "<p>Мне тут попеняли в комментариях, что я совсем забросил раздел, в котором я пишу на разных языках программирования американскую считалочку про пиво на стене. Я почему-то думал, что недавно что-то в него добавлял, а оказалось, что с того раза прошло полтора года.<\/p>\n<p>Пора возобновить.<\/p>\n<p><b>76. SectorC<\/b> — очень <a href=\"https:\/\/web.archive.org\/web\/20230609141452\/https:\/\/habr.com\/ru\/companies\/ruvds\/articles\/740310\/\">маленький компилятор<\/a> подмножества языка Си. Помещается в один сектор — 512 байт, отсюда и название. Язык очень сильно урезанный, но всё равно меня поражает как автор запихнул имеющееся в такой малый объём.<\/p>\n<p>В языке нет строк и операторов ввода-вывода, но в стандартной библиотечке языка (часть которая написана в машинных кодах), есть функции, позволяющие выводить символы, используя их коды.<\/p>\n<p>Из-за этого моя программа было сильно распухла, но я немного заморочился и закодировал строки в целых шестнадцатибитных числах — это единственный тип, помимо указателя из доступных. В итоге объём сильно сократился по сравнению с первой версией.<\/p>\n<p>Самые распространённые в выходном тексте буквы я кодирую полубайтом от <tt>0x1<\/tt> до <tt>0xE<\/tt>, а более редкие — байтом от <tt>0xF1<\/tt> до <tt>0xFC<\/tt>. Завтра попробую описать это чуть подробнее.<\/p>\n<p>Немного так же печалит, что нет локальных переменных, параметров и возвращаемых значений — всё передаётся через глобальные переменные, как в ранних Бейсиках. Кроме того, немного по-особенному расставляются пробелы. Так надо для упрощения разбиения компилятором программы на токены.<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">\/\/ Written by Evgeny Stepanischev https:\/\/bolknote.ru\r\n\/\/ SectorC\r\n\r\nint c; \/\/ input\r\nint shift; int x;\r\n\r\nvoid x() { if( c == ( x &gt;&gt; 8 ) ){ print_ch = x &amp; 255; } }\r\nvoid p() { print_ch = c; print_char(); }\r\n\r\nvoid c2c() {\r\n    c = c + shift;\r\n    x =  288; x(); x =  613; x(); x =  879; x(); x = 1140; x(); x = 1388; x();\r\n    x = 1634; x(); x = 1902; x(); x = 2145; x(); x = 2419; x(); x = 2674; x();\r\n    x = 2918; x(); x = 3191; x(); x = 3338; x(); x = 3684; x(); x = 4200; x();\r\n    x = 4396; x(); x = 4654; x(); x = 4981; x(); x = 5204; x(); x = 5481; x();\r\n    x = 5739; x(); x = 6000; x(); x = 6253; x(); x = 6471; x(); x = 6734; x();\r\n    x = 7033; x();\r\n\r\n    if( c != 15 ){ print_char(); }\r\n    shift = 0; if( c == 15 ){ shift = 15; }\r\n}\r\n\r\nint s; \/\/ input\r\nvoid ps() {\r\n    while( s ){\r\n        c = s &amp; 15;\r\n        c2c();\r\n        s = s &gt;&gt; 4;\r\n        if( s == ( 0 - 1 ) ){ s = 0; } \/\/ signed workaround\r\n    }\r\n}\r\n\r\nvoid bottle()  { s = 17249; ps(); s = 596; ps(); }\r\nvoid of_beer() { s = 6961; ps(); s = 41510; ps(); }\r\nvoid on_wall() { of_beer(); s = 5937; ps(); s = 8692; ps(); s = 22721; ps(); print_char(); }\r\nvoid o_more()  { s = 40723; ps(); s = 675; ps(); }\r\n\r\nint b; \/\/ input\r\nvoid bottle_b() {\r\n    if( b == 0 ){ s = 55; ps(); }\r\n    if( b &gt; 0 ){ print_num = b; print_u16(); }\r\n    bottle();\r\n    if( b != 1 ){ c = 115; p(); } \/\/ s\r\n}\r\n\r\nvoid main() {\r\n    shift = 0; b = 99;\r\n\r\n    while( b &gt; 0 ){\r\n        bottle_b(); on_wall();\r\n\r\n        s = 303; ps(); \/* &#039;, &#039; *\/ bottle_b(); of_beer();\r\n        c = 46; p(); \/\/ &#039;.&#039;\r\n        print_newline();\r\n        b = b - 1;\r\n\r\n        s = 2143; ps(); s = 4735; ps(); s = 4723; ps(); s = 31806; ps();\r\n        s = 59265; ps(); s = 35057; ps(); s = 409; ps(); s = 5231; ps();\r\n        s = 936; ps(); s = 59215; ps(); s = 303; ps();\r\n\r\n        bottle_b(); on_wall(); c = 46; p(); \/\/ &#039;.&#039;\r\n        print_newline(); print_newline();\r\n    }\r\n\r\n    c = 78;  p(); \/* &#039;N&#039; *\/ o_more(); bottle(); c = 115; p(); \/\/ &#039;s&#039;\r\n    on_wall();\r\n    s = 28975; ps(); \/* &#039;, n&#039; *\/ o_more(); bottle(); c = 115; p(); \/\/ &#039;s&#039;\r\n    on_wall();\r\n    c = 46;  p(); \/\/ &#039;.&#039;\r\n    print_newline(); \r\n\r\n    s = 5039; ps(); s = 16692; ps(); s = 4639; ps(); s = 41801; ps();\r\n    s = 30738; ps(); s = 1566; ps(); s = 53071; ps(); s = 913; ps();\r\n    s = 4767; ps(); s = 41887; ps(); s = 4850; ps();\r\n    b = 99; bottle_b(); on_wall(); c = 46; p(); \/\/ &#039;.&#039;\r\n}<\/code><\/pre>",
            "date_published": "2023-06-12T01:05:16+05:00",
            "date_modified": "2023-06-14T18:15:51+05:00",
            "tags": [
                "99",
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Mon, 12 Jun 2023 01:05:16 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120360",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        }
    ],
    "_e2_version": 4079,
    "_e2_ua_string": "Aegea 11.0 (v4079e)"
}