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