# Декабрьское приключение @continue 2025-го Есть такая интернет-забава - "Декабрьское приключение". Нет каких-то строгих правил, просто ежедневная публикация журнала работы над каким-то проектом (не обязательно одним). => https://eli.li/december-adventure December Adventure => gemini://freeshell.de/tags/_decemberadventure #DecemberAdventure В (пока непубличном) репозитории я делаю новый проект p386ilo: запуск системы Konilo на Pocket 386. Здесь я буду публиковать что именно сделано за день. На момент старта декабрьского приключения у меня уже есть минимальное ядро, написанное на ассемблере, которое исполняет виртуальную машину ilo и обеспечивает первоначальную поддержку вывода текста на экран. Пока что всё это тестируется запуском qemu-system-i386. => https://konilo.org/ Konilo: a personal computing system in Forth => gemini://any-key.press/p386/ Pocket 386 author: @continue licence: UNLICENSE ## 2025-12-01 20:00 MSK Сделал python-скрипт тестирования ядра виртуальной машины: run_konilo_tests.py Скрипт принимает в качестве аргумента командной строки путь к репозиторию Konilo. При работе скрипта разбирается файл test-ilo/run-tests.sh. Из него извлекается набор тестов и ожидаемых значений. Тестирование реализуется запуском qemu. Базовый набор тестов проходит без сюрпризов. Плюс сделал клавиатурный ввод. Чтение scan-кодов у меня уже было реализовано, добавил таблицы преобразования для обычного состояния и с нажатой клавишей Shift. Теперь можно запускать первые утилиты. Но они все требуют поддержку ESC-последовательностей при выводе текста. Видимо это и станет моей задачей на завтра. ## 2025-12-02 20:00 MSK Всё же решил начать с функции позиционирования курсора в текстовом режиме, а то вводить команду "в пустоту" немного обескураживает. Оказалось, что если в байт атрибута символа положить 0, то курсор не будет отображаться в этой позиции (почти час убил на отладку этого, обидно). Попутно сделал функцию очищения экрана, которая всё равно понадобится для ESC-последовательностей. В этом направлении пока только немного погрепал логи работы konilo на предмет того какие именно управляющие команды делать в первую очередь. Видимо задача минимум это J (очистка экрана), H (управлением позицией курсора) и m (установка цветовых атрибутов текста). Но начать нужно с того, что полностью пропускать неподдерживаемые ESC-последовательности. ## 2025-12-03 20:00 MSK Сделал перемещение курсора при обработке ESC-последовательностей. Уже стало совсем похоже на работающую систему. Затем добавил раскрашивание текста. С одной стороны я не считаю это какой-то необходимостью, но с другой реализация довольно простая, поэтому "почему бы и нет". / И вот уже есть расхождение с native-реализацией konilo. Допустим мы стоим в первой колонке первой строки. Нам присылают на вывод 80 символов (а размер экрана как раз 25 строк по 80 символов). И восемьдесят первым символом приходит перевод строки (\n). В моей реализации курсор окажется на третьей строке, а в native-реализации konilo на второй. Как по мне: моя реализация "правильней". Пока мне это жить не мешает, поэтому оставлю всё как есть. ## 2025-12-04 20:00 MSK Сегодня совсем скромно: сначала пофиксил работу с клавиатурой, а затем хотел нахрапом сделать запись блока из существующего кода чтения. Поменял команду чтения на запись (0x20 -> 0x30), инструкцию insw на outsw (соответственно edi на esi). Не заработало :( Разбираться сегодня времени нет, к сожалению. ## 2025-12-05 23:00 MSK Инструкцию по превращению чтения секторов в запись "нахрапом" я подсмотрел на wiki.osdev.org: > To write sectors in 28 bit PIO mode, send command "WRITE SECTORS" (0x30) to the Command port. А надо было сразу подсмотреть в colorForth и просто добавить поллинг бита BSY: ``` : read 20 sector 256 for rdy insw next drop ; : write bsy 30 sector 256 for rdy outsw next drop ; ``` Плюс не зря qemu писал предупреждение о необходимости явно указать формат образа жёсткого диска как raw. В итоге запись секторов заработала. ## 2025-12-06 22:00 MSK В документации Konilo нет конкретных требований (или даже рекомендаций) по обработке переполнения адреса памяти (или, например, стека). Поэтому я принял политику из uxn, где память циклична. Я уже это применил к инструкциям cy и cp. Сегодня решил переписать функции чтения и записи блоков. Обработал ситуацию, когда адрес очередной ячейки (а блок это 1024 ячейки) выходит за пределы адресуемой памяти. При работе с диском в PIO режиме фикс оказался тривиальным. На текущий момент мне известно одно последнее место, требующее отладки и доработки. Не уверен, правда, что завтра у меня будет время им заняться. А уже после исправления можно будет готовить публичный репозиторий. ## 2025-12-07 22:00 MSK Код сегодня вообще не трогал. Зато запустил текущую версию на реальном Pocket 386! Ух как всё тормозит :) Загрузка среды занимает чуть менее шести минут. Запуск команды manual занимает что-то около 45 секунд. Причём почти пять уходит на отрисовку экрана: выглядит посимвольный вывод содержимого ретро-футуристично, но оставлять это так нельзя. Не знаю насколько мне удастся докрутить со стороны виртуальной машины. У меня есть серьёзные подозрения, что сама высокоуровневая forth-система требует оптимизации. ## 2025-12-08 22:00 MSK Начал день с фикса мелкого бага, который заключался в том, что позиционирование курсора в некоторых случаях запаздывало на один ход. Оставшуюся часть дня посвятил изучению возможных способов профилирования. Нужно проверить на железе Pocket 386 гипотезы с доступом к RTC через CMOS и со счётчиком PIT. ## 2025-12-09 18:00 MSK И RTC (с доступом через CMOS), и PIT работают на Pocket 386 с запрещёнными прерываниями ЦП. Это радует, можно профилировать и оценивать результат как длительных операций, так и довольно коротких. Начал я с того, что вспомнил, что видел один твик в ilo-vm/ilo.py. В виртуальной машине ilo все адресуется четырехбайтовыми ячейками, а инструкции занимают один байт. Поэтому в каждую ячейку "упаковывается" четыре инструкции, а затем исполняется каждый байт. Но в качестве оптимизации можно рассматривать одну ячейку как самостоятельную инструкцию. Я собрал статистику по исполняемым четырехбайтовым "инструкциям" на момент загрузки Konilo. И с отрывом в десятичный порядок по количеству исполнений (1`944`047 раз) лидирует 0x04060205. То есть pu du po sw. А это обычное слово OVER в классическом Forth: ``` : OVER ( a b -- a b a ) >R DUP R> SWAP ; ``` И суть оптимизации тут вполне очевидна: вместо того, что бы перекидывать ячейки между двумя стеками моя нативная реализация сделает это всё, используя регистры центрального процессора. ## 2025-12-10 20:00 MSK Как я и думал: все оптимизации нужно проверять на реальной железке. Оптимизация с OVER прошла "на ура": время загрузки Pocket 386 сократилось на 20 секунд. Оптимизация других четырехбайтовых "инструкций" дают уже не такой впечатляющий результат: 1-2-3 секунды. Хорошо, взял четырехбайтовую инструкцию из топа при запуске команды manual. Команда manual стала на 2 секунды запускаться быстрее, но зато время начальной загрузки... увеличилось на пару секунд! В общем: возиться дальше с четырехбайтовыми инструкциями ради минимального прироста производительности нет никакого желания. Следующим шагом я выбрал "резервирование" регистров ЦП. Для эксперимента я выбрал регистр IP виртуальной машины ilo и вместо того, что бы располагать его в памяти, перенёс его в "физический" регистр edx. Стало быстрее даже при запусках на qemu. На физическом железе Pocket 386 ещё не запускал, так как у него села батарея, а зарядку я забыл взять с собой. ## 2025-12-11 20:00 MSK Пробовал разные варианты дальнейших оптимизаций. Например хранить в cl и ch указатели стека данных и стека возвратов. Но остановился на том, что cl расположил указатель стека данных, а остальные часть регистра ecx всегда 0. Плюс задал размер стека данных в 256 ячеек (то есть любое значение cl валидно). Это позволило сделать простой инкремент / декремент указателя (inc cl / dec cl) и простое обращение к элементам массива (mov eax, [ecx*4 + ILO_DATA_STACK]). Суммарно это позволило сократить время загрузки системы до 3 минут 20 секунд. Тормоза всё ещё раздражают, но этим уже почти можно пользоваться (с некоторыми оговорками). И это не пустые слова: я сегодня читал страницы руководства Konilo на Pocket 386 во время поездки в метро ;) (правда не через команду manual, а редактором tuhi, благо он работает вполне сносно). ## 2025-12-12 20:00 MSK Начал понемногу ковырять Forth-часть Konilo. Уменьшил количество блоков в системе (с 4096 до 2000), чем ускорил загрузку секунд на 10. Плюс изменил размеры текстового терминала: ширину строки поставил 79, что бы избежать проблемы, описанной выше (с переносом строк). Попутно понял что тормозит не manual относительно tuhi, а код раскрашивания синтаксиса. Если в tuhi открыть блок с Forth кодом, то отрисовка занимает секунд 10 (а открытия блока с просто текстом - 1 секунда). (хотя старт команды manual всё ещё занимает секунд 30)