Архитектура операционной системы для ZX Spectrum-совместимых компьютеров
NedoOS – многозадачная операционная система для «русского ZX Spectrum» со средами программирования на ассемблере, Basic, Pascal, C, NedoLang. Работает на TR-DOS, FAT16 и FAT32 с длинными именами, поддерживает tar, gz, zip, rar2 и практически все реально используемые форматы спектрумовских файлов, сетевые утилиты включают Web-браузер и Web-сервер, Telnet-клиент и Telnet-сервер, IRC-клиент и др. Под ОС пишутся игры, в том числе сетевые. Сейчас в репозитории 6 участников. Исходный код всей системы (58 программ) составляет 230 тысяч строк на ассемблере и 70 тысяч строк на Си.
Архитектура компьютера
ZX Spectrum – 8-битный компьютер на базе процессора Z80. От прочих подобных компьютеров отличается тем, что вместо специализированного видеоконтроллера у него – просто комбинация счётчиков и мультиплексоров (в оригинале собранная в БМК, но легко переводимая на «рассыпуху» – микросхемы мелкой логики). За счёт этого компьютер сначала победил в ценовой гонке и стал машиной по умолчанию для «bedroom programming», а потом легко убежал от оригинального производителя и завоевал важнейшее место в компьютеризации многих стран мира. В том числе – стран, бывших в составе СССР, где Spectrum-совместимые машины выпускались околомиллионными тиражами (во всяком случае, больше, чем БК, ДВК и УКНЦ, вместе взятые).
Потрясающий воображение список известных клонов – тут. «Открытость» архитектуры (с точки зрения паяльника – а без паяльника наши клоны даже к телевизору было подключить проблематично) превратила ZX Spectrum в весьма навороченную платформу, с нетипичными для других 8-битных платформ (которые уже мертвы или заспиртованы) мегабайтами, мегагерцами, винчестерами и сетевыми картами.
ZX Spectrum до сих пор работает как платформа для инди-игр (см. идущие сейчас конкурсы «Yandex Retro game battle» и «Твоя игра 6», дедлайн которого продлён до 1 декабря), но разработка уже редко происходит в первоначальном формате, т.е. на самом Спектруме. Это объясняется среди прочего тем, что нативные среды разработки, написанные под TR-DOS в 90-х и 2000-х, отстали от жизни, а на смену им не пришло ничего нового.
Необходимость операционной системы
Операционные системы для Спектрума пишутся с давних пор. Операционная система, которая сидит в ПЗУ, позволяет писать на языке Бейсик и возиться с магнитной лентой. Дисковые операционные системы TR-DOS, GDOS, +3DOS и т.п. добавляли в Бейсик команды работы с дискетой, однако серьёзными утилитами обросла только TR-DOS, причём не за счёт довольно неуклюжего бейсик-интерфейса, а за счёт документированных (и не очень) точек входа в ПЗУ TR-DOS.
Если не считать нескольких версий CP/M (которая изначально писалась не для Спектрума и не использует по достоинству его возможности), первой серьёзной спектрумовской ОС была однозадачная iS-DOS (раз, два, три, архив), разработанная в Санкт-Петербурге в самом начале 90-х. Она «из коробки» предоставляла командер и файловую систему с подкаталогами (ни с чем не совместимую), редактор текстов произвольного размера, много дисков утилит и возможность расширения своими резидентами. К сожалению – расширения только в пределах окна ОЗУ размером 48K. Первые версии iS-DOS довольствовались 41K под всю систему, резиденты и программу пользователя, остальное пространство ОЗУ занимал экран размером 6912 байт.
iS-DOS в естественной среде обитания
Последние версии, до сих пор выходящие под названием TASiS (раз, два) и заточенные под клон ATM-Turbo 2+, позволяют развернуться на все 64K адресного пространства, убрать экран из памяти и переключать странички ОЗУ, но ядро системы в адресном пространстве всё равно остаётся. (То же касается и CP/M – во всех известных версиях система занимает верхние адреса и неистребима.)
Предпринимались и другие попытки написания ОС. К сожалению, ни одна из таких ОС не получила заметного распространения, а многие и вовсе не были закончены.
Уже упомянутый iS-DOS получил распространение в Санкт-Петербурге и Москве. Примерный функциональный аналог iS-DOS на TR-DOS диске – X-DOS – остался в пределах Кирова.
X-DOS и её командная строка
Многооконная система ДОМЕН ОС/Pink Floyd, также из Санкт-Петербурга, – не дожила до юзабельного состояния. В частности, жёсткие диски она не поддерживала, и в ней не было никаких средств разработки.
ДОМЕН ОС и её документация в HTML
Харьковская система DNA OS, в основном заточенная на копирование файлов, принесла определённую пользу хозяевам «винтов», но «софт под винт» в ней так и не появился.
DNA OS и её файловый менеджер
Не дожили до жизнеспособных релизов московская NK-DOS, братская NeOS, гродненская DOORS\AQUA и саранская ZX-OS/ZXRTK. Из многозадачных ОС ближе всего к пользователю подошли многооконные MythOS (Днепропетровск) и ChaOS (Таганрог), но и они закончили свою жизнь в исходниках — тоже без поддержки жёстких дисков и без средств разработки.
MythOS и её консоль
ChaOS и её хаос
Почему же было так много неудач?
Аппаратные требования для многозадачной операционной системы
Проблема всех ранних ОС для Спектрума была в том, что разработчики пытались обеспечить своему творению максимальную совместимость. Например, на iS-DOS – с машинами начиная с ZX Spectrum 48K (с дисководом или HDD), а на большинстве последующих систем – с ZX Spectrum 128K. Это накладывало неизгладимый отпечаток на распределение памяти.
Дело в том, что на 48K-машинах полное адресное пространство Z80 размером 65536 байт разделялось на 2 части: 48K ОЗУ (из которых 6912 байт отъедал экран, что оставляло 41 килобайт под всё остальное) и 16K ПЗУ Бейсика, от которого реальной пользы для ОС нет. В 128K дела обстояли лучше, но ненамного. Экран из адресного пространства Z80 там можно убрать (выбрав второй видеобуфер), но переключать страницы ОЗУ можно только в верхней четвертинке (с адреса 0xc000) адресного пространства. Остальные 32K ОЗУ не переключаются. А нижние 16K так и продолжают содержать ненужное для операционной системы ПЗУ. Конечно, фирменный 128K позволяет внешним устройствам (таким как LEC Memory Extension, DISCiPLE/+D, MB-02) подменять нижние 16K, а его потомки, начиная с +3, обычно имеют тот или иной встроенный порт для этого (чаще всего там доступна только одна страница ОЗУ), но половина адресуемой памяти всё равно не может меняться с переключением задач.
Представьте, что вы пишете текстовый или графический редактор под операционную систему для ZX Spectrum 128K. Очевидно, верхние 16K – слишком маленький объём для текста или иллюстрации (речь не о стандартных «экранах» в 6912 байт), и надо либо постоянно подгружать файл с диска (что медленно и неудобно), либо раскидать документ по страницам. Где же в этом случае будет располагаться код самого редактора, его переменные и стек? Не в верхних 16K ОЗУ, потому что там документ. Не в нижних 16K, потому что там, скорее всего, ПЗУ. Выходит, всем задачам придётся делить между собой один общий блок в 32K ОЗУ. Релоцируемые резиденты, разбитые, допустим, по 256 байт, вызывают процедуры из верхнего ОЗУ, отдельные резиденты – буферы для работы с данными, стек тоже ограничен 256 байтами… Это очень неудобно для разработчиков. Результат: под такие системы писали только сами авторы, да и у них хватало запала только на год-два, чего недостаточно даже для создания среды разработки.
Таким образом, для удобной многозадачной системы на Спектруме жизненно необходимо иметь переключаемые страницы ОЗУ в каждой из четвертинок (выбор именно четвертинок по 16К традиционен для Speccy) адресного пространства Z80. Спектрумовских схем, которые умеют это делать, немного: это малоизвестная «ZX-MMU» от fk0 (2000) и более-менее распространённая ATM-Turbo 2(+) (1992, 1993):
Из документации. Установленный бит 6 включает управление через порт #7ffd (ПЗУ TR-DOS автоматически подменяет ПЗУ 48 Бейсика при исполнении кода в #3dxx), в противном случае бит 7 просто включает заданную страницу ПЗУ. «ROM2» — бит 4 порта #7ffd, он работает всегда.*
Многие авторы операционных систем полагали, что такое требование к подсистеме памяти – уметь переключать всё – сильно ограничит число пользователей, и не принимали его. В результате пользователей у них получалось примерно 0 – из-за отсутствия софта.
Предыстория - что такое NedoPC
В 2002-2003 годах на просторах трёх континентов родилось движение NedoPC, которое кратко можно описать как… эмм… разработку компьютеров из подручных средств. И начали NedoPC с того, что другие на тот момент закончили – производить Спектрумы и устройства для них. А в качестве Спектрума взяли тот самый Turbo 2+ (1993) – потомок ATM-Turbo 2 (1992), который потомок ATM-Turbo (1991), который потомок Pentagon 128 (1990), известного в узких кругах как АТМ 128 (историю борьбы фирм в лихие 90-е можно прочитать в описании к Honey Commander’у). В своё время Turbo 2+ (к которому уже автоматически приклеилось обозначение ATM, как в прошлых версиях) замахивался чуть ли не на замену IBM PC ранних комплектаций – с HDD (потом и CD-ROM), аналогом EGA, XT-клавиатурой (потом и AT), COM-портом, ЦАП, АЦП (также игравшими роль модема) и играми «Принц Персии» и «Gobliiins», передранными абсолютно точно с PC-версии (не упоминаю весь софт, его много). Но часть остроумной схемы была упрятана в ПЛМ, из-за чего скопировать её мог мало кто. На просторах Украины раздавались слухи о замене ПЛМ на ПЗУ и о копировании отдельно контроллера HDD, но в общем и целом компьютер в 90-е встречался куда реже «Пентагона». К счастью, потом разработчики Turbo 2+ – фирма МикроАРТ – передали всю документацию в свободное пользование.
Практически сразу в орбиту NedoPC втянулось несколько разработчиков софта, и пошла речь об операционных системах. Иначе ответ на вопрос «куда нам плыть» не получался. Первым плодом тяжких раздумий в 2005 году была уже упомянутая система TASiS, ради которой были приобретены исходники iS-DOS.
Параллельно, в 2007 году, один из авторов этой статьи создал виртуального персонажа SMAN, который как раз и разрабатывал многозадачную операционную систему. И да, она тоже прошла через этап «резидентов в нижнем ОЗУ». Этот этап затянулся на годы, в 2016-2017 был даже написан компилятор Си-подобного языка NedoLang, влезающий в 48K (также компилирует под ARM Thumb). Гордиев узел был разрублен только с появлением графического редактора Scratch (2018).
Scratch и его меню
Выбор в 2018 году было совершить уже легко: совместимость с ATM-Turbo 2+ была доступна всем пользователям – если не на оригинальной железке и не на NedoPC’шных ZX Evo и Pentagon 2.666LE, то на ATM3 от Zorel’а или хотя бы в эмуляторе (Unreal Speccy, Xpeccy, ZXMAK2, Es.pectrum и др.).
Turbo 2+ (1993, но плата явно новее)
Pentagon 2.666LE (2009)
весьма редкая машина
ZX Evolution (2009)
можно купить в любой булочной
ATM3 (2017)
достать можно только по знакомству или изготовить самому
С этого момента система получила название NedoOS и стала развиваться взрывообразно.
Надо отметить, что для сдерживания фантазий на первоначальном этапе этого взрыва система была построена в основном на командах, совместимых с CP/M и MSX-DOS. До сих пор список команд разбит на три раздела: совместимые с CP/M (сейчас их 13, их использование не рекомендуется из-за неудобной системы FCB), совместимые с MSX-DOS (их 11, там имя файла уже идёт текстом, а вместо структур FCB используются числа — хэндлы) и несовместимые (в основном нефайловые, их сейчас 48, но некоторые планируется выбросить).
Система разработки на текущий момент
Исходные тексты NedoOS всё ещё не адаптированы под имеющиеся в системе компиляторы (NedoLang и его ассемблер NedoAsm, Turbo Pascal и BDS C). Пока только 5 утилит компилируются внутренними инструментами. А основная часть пакета собирается на PC с помощью SjASMPlus (основной код) и Z80 IAR C (файловая система FatFs и некоторые утилиты).
Файлы размещаются на выделенном SVN-сервере и состоят из исходников, бинарного релиза и коллекции тулзов для сборки на PC.
Система собирается одной кнопкой (батник или Makefile – две системы сборки). Можно также одной кнопкой отдельно собрать один из таргетов (под HDD, SD-карту и т.п.) и сразу запустить эмулятор. Можно отдельно собрать каждую утилиту батником нижнего уровня. Две системы сборки получилось потому, что некоторые разработчики NedoOS сидят в Windows, а некоторые – в Linux.
Кроме компиляторов имеются также командный процессор cmd.com (может исполнять *.bat) и написанный с нуля интерпретатор NedoBasic с графическими возможностями.
NedoLang компилирует себя
Устройство ядра
Часть NedoOS, видимая программе пользователя, – это ряд вызовов в области памяти 0x0000..0x0038, так называемый керналь. Последний из этих вызовов – обработчик прерывания 50 Гц, а далее с 0x0080 следует командная строка с параметрами для программы. Пока ещё устройство керналя совместимо с CP/M (вызов через 0x0005), но мы потихоньку от этого отходим (CP/M-вызовы используются только в 4 программах из 58). Все системные вызовы организованы как макросы (программы используют модуль /src/_sdk/sys_h.asm с этими макросами и константами) и могут быть существенно изменены по мере развития ОС. Следствием этого является необходимость пересборки всей системы и прикладного софта при каких-либо изменениях в этих макросах – но, как мы видели, это делается одной кнопкой.
Внутренняя часть NedoOS спрятана в страницах ОЗУ и содержит:
- обратные стороны вызовов кернеля;
- продолжение обработчика прерывания с планировщиком задач (шедулером);
- системный обработчик прерывания, он без планировщика, зато вызывается всегда — читает клавиатуру, мышь и часы, по комбинации клавиш переключает «фокус» (см. ниже), выставляет палитру и видеорежим;
- набор файловых и управляющих функций (BDOS), вызываемый через call 0x0005, как в CP/M и MSX-DOS;
- драйверы блочных устройств (SD-карта на Z-Controller, SD-карта на NeoGS, IDE HDD, дискета, USB flash на плате ZXNetUSB);
- драйвер клавиатуры (обычная 40-клавишная или PS/2-клавиатура ZX Evo);
- драйвер мыши (Kempston mouse с колёсиком);
- драйвер часов реального времени (по схеме Mr. Gluk), также система предоставляет время от начала работы в 50-герцовых квантах;
- драйвер сетевой карты (Wiznet W5300 на ZXNetUSB);
- файловую систему FatFs для FAT-16 и FAT-32;
- файловую систему TRDOSFS для TR-DOS дискет и RAM-диска;
- описатели смонтированных файловых систем;
- описатели открытых файлов FatFs, открытых файлов TR-DOS и открытых очередей (пайпов);
- описатели запущенных задач.
Все драйверы вкомпилированы в ядро, поэтому есть несколько разных “таргетов” сборки системы (ATM2, ATM2+HDD, ATM3, ZX Evo, Pentagon 2.666LE).
Обработчик прерываний, который располагается в пространстве пользователя, выглядит так:
push af
push bc
push de
user_fdvalue6=$+1
ld a,fd_system ;зависит от текущей страницы экрана
out (0xfd),a ;уходим в контекст ядра (короткая адресация порта допустима на этом железе), там сохраняются прочие регистры и происходит переключение задач
;---------
;после выхода из контекста ядра:
;bc=memport0000
;d=pgmain
out (c),d ;may switch this code page
curpg16k=$+1
ld a,0
ld b,memport4000/256
out (c),a
curpg32klow=$+1
ld a,0
ld b,memport8000/256
out (c),a
curpg32khigh=$+1
ld a,0
ld b,memportc000/256
out (c),a
pop de
pop bc
pop af
ei
ret
Переключение задач происходит по кругу (пропускаются замороженные задачи), приоритетов пока нет. Но задача idle выполняется только тогда, когда все остальные либо заморожены, либо в текущем кадре сами вызвали YIELD (системный макрос YIELD – отдать время системе до следующего прерывания, системный макрос YIELDKEEP – отдать время системе с возможностью возврата управления когда угодно).
Задача может сменить обработчик прерываний, можно с возвратом в него и произвольным кодом после него. Для этого есть определённые рекомендации и примеры.
Система терминалов
Задачи делятся на три типа:
- те, которым экран вообще не нужен,
- те, которым достаточно текстового терминала,
- графические – с собственным экраном требуемого разрешения и собственной палитрой.
В NedoOS, помимо понятия текущей задачи, имеется понятие «фокус» (доступ к клавиатуре, мыши и экрану). Фокус между задачами переключается вручную (по комбинации Symbol Shift+Enter) или автоматически (при включении графики, выключении графики или закрытии задачи).
Задачи типа (1) никогда не получают фокус. Более того, задачи типа (2) тоже не получают фокус, вместо них фокус имеет текстовый терминал, к которому они привязаны. А привязаны они через потоки stdin и stdout, по которым гуляют буквы, цифры, закорючки и коды терминала VT-100 с некоторыми дополнениями. Stdin и stdout реализованы через библиотеку stdio.asm как очереди (пайпы). В ядре очередь имеет буфер в 255 байт. Функции записи и чтения из очереди возвращают, сколько реально прочитано или записано (как и для файлов) и не закрыта ли очередь с другого конца. Эти потоки наследуются от отца к сыну. Но в конечном итоге отображается всё терминалом term.com, который можно скроллить и даже копипастить. Есть также сетевой Telnet-сервер netterm.com. Этот netterm.com можно и не запускать, а вот term.com запускается автоматически при старте системы.
Nedovigator в терминале, а терминал прокручивается колесом мыши
Для задач типа (3) речи о рисовании пикселей через систему, конечно, не идёт – для этого не хватает мегагерцев Z80 (у большинства пользователей их всего 14). Поэтому рисовать надо через память. А поскольку экранных областей на ATM всего две (как и на 128K), только одна задача может иметь собственные страницы экрана. Страницы экрана перехватываются при смене фокуса, а вместо них задаче-лузеру отдаётся фальшивый номер страницы – страница, которую можно просто портить. Когда задача получает фокус, ей приходит об этом отдельное сообщение как бы от клавиатуры, но не соответствующее ни одной реальной кнопке. Это позволяет задаче вовремя восстановить экран. Ещё задача может включить режим, когда система сама хранит за неё экран и восстанавливает его.
Командная строка
Функции командной строки реализует утилита cmd, которая автоматически запускается при запуске системы. Точнее, первая задача idle запускает term с параметрами cmd.com autoexec.bat. Это значит, что cmd выполнит указанный батник. Команды, прописанные там, выполняются по порядку. По умолчанию команды выполняются с ожиданием окончания, но если команда – это запуск программы, то перед ним можно поставить start – программа будет работать в фоновом режиме (только это не совместимо с концепцией наследования stdin и stdout – данные в очередях могут быть перемешаны).
После окончания операции, указанной в параметре (если параметр вообще был), cmd обычно завершается. Исключение оставлено только для autoexec.bat, чтобы после его окончания не остаться с разбитым корытом. Правда, в случае чего idle ожидает одновременного нажатия C+M+D и может перезапустить term с cmd.
Работа в командной строке напоминает MS-DOS. Можно перенаправить вывод запущенной операции в файл (например, dir > filename) или в другую программу (например, dir | more.com), можно перенаправить ввод (например, more.com < filename). Но передача по цепочке пока не поддержана.
Для более удобной работы с файлами используется командер Nedovigator (nv.com).
Котик, потому что Nedovigator вы уже видели
Размещение системы
Надо уточнить, что каждая файловая система у нас примонтирована к определённой букве, так что пути выглядят как m:/bin/filename.txt. Очереди имеют букву монтирования z:, но пока открываются без имени — связь между задачами идёт путём передачи номера (хэндла) очереди.
Чтобы избежать специализированных загрузчиков типа GRUB, NedoOS надеется на то, что в ПЗУ компьютера имеется возможность запуска хобетного файла (*.$C) с нужного устройства — например, в ZX Evo файловым браузером или по кнопке 5 в главном меню, а на Pentagon 2.666LE – через встроенный копировщик Fatall. В противном случае придётся грузить ОС с TR-DOS дискеты. Системные файлы располагаются в каталоге /bin системного диска, игры лежат в /nedogame. Дополнительные файлы игр обычно размещаются в подкаталоге, одноимённом игре: например, раскрашенная под NedoOS игра /nedogame/br.com (Чёрный Ворон) имеет также каталог /nedogame/br с графикой, музыкой и уровнями.
Цветной Ворон
Текущие возможности
Память: поддерживается до 4M ОЗУ (настраивается в системе сборки), память выдаётся задачам по запросу страницами по 16K, задачи могут и сами их возвращать в систему. Из-за отсутствия защиты памяти в железе всю память можно считать разделяемой. По умолчанию страницы выделяются с начала памяти, не считая нескольких системных страниц. Своп не поддерживается (он рассматривался на ранних этапах, но требует перенумерации страниц, что сильно снизит производительность обычных задач).
Многозадачность: вытесняющая (по прерыванию 50 Гц) и кооперативная; одновременно может быть запущено до 16 задач (одна из них – idle, её можно снять, но тогда YIELD не даёт гарантий); можно подставить свой плейер музыки в системный обработчик прерываний – будет выполняться в контексте ядра с тремя страницами пользователя (переключать их на лету пока нельзя). Задачи могут быть в замороженном состоянии (если они только что созданы, удаляются или ожидают потомка). Можно в cmd смотреть список задач, их активность и наличие графического режима, снимать с исполнения (при этом освобождаются их страницы, файловые записи FatFs и сетевые сокеты).
Файлы: можно одновременно открыть до 16 файлов FAT, 8 файлов TR-DOS и 8 очередей. Можно одновременно читать и писать несколько файлов (важно для компилятора) – даже на дискете! Можно считывать параметры файла, указатель на текущую позицию, менять эту позицию, создавать и переименовывать директории. Очереди предполагают две задачи-пользователя, их требуется открывать один раз (при создании), но закрывать дважды. Файловые операции выполняются в контексте ядра, в это время не работает планировщик задач, но работает системный обработчик музыки.
Работа с сетью: протоколы ICMP, TCP, UDP, одновременно до 8 сокетов. Имеются утилиты ping, time, telnet, netterm (Telnet-сервер), dmirc (IRC-клиент), dmftp (FTP-клиент) 3ws (Web-сервер для копирования и удалённого запуска файлов), NedoBrowser (текстовый браузер с возможностью отдельно смотреть картинки jpg, gif, png, bmp и svg — последние два формата с ограничениями; поддерживает протоколы http и gopher, https через прокси), wget (фоновая скачка файлов, запускается из браузера, умеет автоматически запускать просмотр графики и плейер музыки).
NedoBrowser что-то ищет в Интернете
Работа с архивами: .tar и .rar доступны на чтение и запись (ZXRar – урезанный аналог Rar 2.x под DOS — без ряда кодов, solid архивов, мультимедийного формата и шифрования, ZXUnRar поддерживает все коды и solid архивы, но тоже не поддерживает мультимедию и шифрование), .gz и .zip только на чтение. Пока всё только через утилиты командной строки.
Типы документов: файловые ассоциации прописаны для встроенного командера Nedovigator в файле nv.ext. Сейчас этот файл выглядит так:
bmp:scratch.com
bat:cmd.com
txt,new,ext,ini,nfo,diz:texted.com
gif,jpg,png,htm,svg:browser.com
tfc,pt2,pt3,mt3,m :player.com (pt3 можно редактировать специальной версией Pro Tracker под NedoOS)
bas:basic.com
zip,gz :pkunzip.com
trd,scl,fdi,tap:dmm.com (программы TR-DOS и ленточные — они запускаются на ZX Evo путём монтирования образа, их можно переключать, сохранять и выключать по кнопке Magic; то есть можно, например, играть в игру и периодически переключаться и писать описание)
16c,scr,fnt,img,3 ,888,y ,+ ,- ,plc,mc ,mcx,grf,ch$,mg1,mg2,mg4,mg8,rm ,mlt:view.com
mod:modplay.com
tar:tar.com
sna,b ,z80:nmisvc.com
rar:unrar.com
Имеется также с десяток игр, в том числе эмулятор «Супер Марио» (для работы требуется дамп картриджа), раскрашенный «Eric and the Floaters», частичный порт «Бесконечного лета» и сетевой Snake.
Планы
- Расширение функционала батников (уже передаются параметры)
- IDE и self-hosting
- Слои в графическом редакторе
Заключение
На текущий момент в NedoOS реализован весь нужный костяк функций, так что при наличии нужного железа системой удобнее пользоваться, чем не пользоваться.
Но впереди ещё много работы. Идеал тем ближе, чем больше разработчиков подключится к проекту.
Источник: https://habr.com/ru/post/521012/