Куда плыть дальше
Alone Coder
Компилятор уже собирает себя сам. В нём
уже можно релизить релизы.Это уже продукт.
Молодой сотрудник, на которого я возла─
гал надежды, отказался портировать компи─
лятор на ARM. Хотя раньше обещал, когда
компилятор был маленький и нестрашный.
Придётся самому.
Ускорение самосборки пока что зашло в
тупик. За минуту никак не пересоберёшь.
Появилось ещё только две радикальных идеи:
ё)токенизировать исходники на NedoLang
(ключевые слова и двойные символы)
ж)генерировать obj (с патчами) прямо из
компилятора (тогда нельзя асмовставки и
асмопроцедуры - только асмомодули),сделать
специальный линкер или лоадер.
15.05.2017: пытался ускорить лексер че─
рез чтение строки в буфер. Но проблема с
длинными строками. Если проверять длину,
то скорость та же. Ограничить длину строк?
Пока в исходнике есть весьма длинные стро─
ки. Их и так надо сократить, чтобы можно
было удобно редактировать на Speccy. Я да─
же рисовал самый маленький на свете шрифт
4x2, написал показывалку и тестировал про─
смотр исходника с ним. Результат этого эк─
сперимента я использовал, когда вырабаты─
вал стиль оформления исходника.
17-18.05.2017:
- теперь имена процедур в ассемблере -
без точек в конце,а автометки - с точками.
- теперь _ в начале глобальных идентифи─
каторов не нужно. Локальные метки именно
временно перекрывают глобальные, а потом
удаляются из таблицы меток.
- придумал, как ускорить== и != . Но не
сделал.
- мысли:
Как форвардить переменные: игнорировать
одинаковые - совместимо с Си.
Как форвардить константы? Брать послед─
нюю (можно и как метку, и как переменную).
Это не сработает,если ассемблируем по час─
тям, сработает,если ассемблируем всё в ку─
чу.
Ассемблер должен склеить:
1. модуль неявных математических функ─
ций (компилятору не нужно,но кодогенератор
подклеивает нужный? или скрипт компиляции
меняется в зависимости от таргета?) - под─
ключать в конце, чтобы были только исполь─
зуемые процедуры
2. заголовочный файл ОС (компилятору не
нужно, про этот файл знает только скрипт
компиляции под данный таргет!!!)
3. модуль запуска? или два - начало и
конец? (компилятору не нужно, ditto)
4. код + код + код...
5. переменные+переменные+переменные...
6. строки + строки + строки...
Можно в недоасме исключить неиспользуе─
мые процедуры на втором проходе?
Надо какой-то хитрыйif,который прове─
ряет, есть ли процедура в метках первого
прохода на втором проходе.
Но тогда не получится исключить проце─
дуру, которая используется только в неис─
пользованной процедуре.
(Пока что в NedoAsm нет даже простой
условной компиляции с проверкой существо─
вания метки.)
19.05.2017 - добавил +sizeof(<type>) ,
структуры и константные массивы строк. Те─
перь можно писать игры типа Rusted Souls:
const struct state day1s24={
NOSEL, IMG_STREETDAY,
BIGSPR_PAPERBOYO, NOANIM,
&day1m24, NOMSG, NOMSG, NOMSG
};
const struct msg day1m23={
NOCMD,
&day1s24,
"
3<О, парнишка с газетами...>n
ЧПарень! Газету!"
};
21.05.2017 - утилита nedores, конверти─
рует графические ресурсы для игр.
Подсчёты показали, что если при само─
сборке вся система будет лежать в памяти
(а не грузиться из файлов и захламлять
диск временными файлами,как сейчас),то по─
надобится не48K и даже не 128K , а 276K -
это даже если в памяти только один 32-ки─
лобайтный исходник. А этих исходников на
самом деле210K!
Пока остаёмся на48K.
30.05.2017 - как делать указатели на
процедуры без лишнего тайпкаста? Копался в
typecode.h - свободный бит остался только
один, тратить жалко...
06.06.2017 - оказывается,где-то ещё ос─
тались тайпкасты через/*+*/(PCHAR) .
Исправил.
07.06.2017 - немного сократил ассемблер
за счёт процедурыreadf(fin) с одним пара─
метром вместо кучи.
08.06.2017:
Теперь автометки нумеруются не с.B. ,а
с.A. Так логичнее. А то потом, после ре─
лиза, уже не переделаешь - понапишут своих
библиотек :)
Пытался ускорить токенизатор в компиля─
торе через таблицу меток вместоif-else. А
он от этого замедлился! Оставил так - про─
ще будет локализовать токенизатор под ARM.
И забросил дальнейшие попытки ускорения.
Чтобы было с чем сравнить, про скорость
оригинального Си на PDP-11/70 авторами бы─
ло написано следующее:
"In assessing the costs of using C, the
cost of the compilations themselves has to
be considered.This too we deem acceptable.
For example, to compile and link-edit the
entire operating system ("sys-gen") takes
somewhat over nine minutes of clock time
(of which seven minutes are CPU time); the
system consists of about 12,500 lines of C
code, leading to a rate of about 22 lines
per second from source to executable
object on a PDP-11/70. The compiler is
faster than this figure would indicate;the
system source makes heavy use of "include"
files, so the actual number of lines
processed by the compiler is 38,000 and
the rate is 65 lines per second."
https://archive.org/stream/bstjS7-6-1947/
bstjS7-6-1947_djvu.txt
65 строк в секунду на такой древней си─
стеме против наших 41.
Правда, если вы откроете исходник ори─
гинального компилятора Си (ищите в интер─
нете "Very early C compilers and
language" ), то увидите, что он весь напи─
сан очень короткими строчками без коммен─
тариев:
init(s, t)
char s[]; {
extern symbuf, namsiz;
char symbuf[], sp[];
int np[], i;
i = namsiz;
sp = symbuf;
while(i--)
if ((*sp++ = *s++)==' ') --s;
np = lookup();
*np++ = 1;
*np = t;
}
И в таком же стиле написан UNIX (см.
листинг в книге"Lions' commentary on UNIX
бth edition" ).
Так что в общем мы наравне.
09.06.2017 - сделал инициализацию таб─
лицы меток в токенизаторе один раз, а не
на каждом файле.
12.06.2017 - идея для построения проек─
та:
#include "name.h"
должен выдавать:
include "name.dec"
incobj "name.bin","name.rel" или просто
incobj "name"
это может быть любое место исходника (в
стартапе инклюды в конце)
14.06.2017:
Как компилировать на48K большие прог─
раммы, метки которых не помещаются в ассе─
мблер за один раз? Сейчас мы уже впритык и
развивать компилятор не можем...
а) с линкером:
# сделать экспорт деклараций из асма (и
предусмотреть токен?) СДЕЛАНО
# сделать EXPORT в компиляторе
# сделать ассемблирование цепочки фай─
лов - СДЕЛАН INCLUDE (теперь надо сделать
#include в компиляторе)
# сделать экспорт объектника в асме
(патчи на месте и на будущее патчи назад)
# сделать линкер
б) без линкера:
# сделать экспорт деклараций из асма (и
предусмотреть токен?) СДЕЛАНО
# сделать EXPORT в компиляторе - для
процедур, функций, переменных, констант,
константных строк/массивов/структур
# сделать ассемблирование цепочки фай─
лов - СДЕЛАН INCLUDE (теперь надо сделать
#include в компиляторе)
# сделать подклеивание бинарников с
меткой (вместо nedodefb и для подключения
модулей) СДЕЛАНО
# сделать экспорт таблицы релокации
(или обжа?)
# сделать подклеивание бинарников с
релокацией (или обжей?)
- после этого можно развивать компилятор
и асм
Как отличать в команде, где асм,где би─
нарник? или реализовать incbin (он невло─
женный)? тогда сделать USEUNIT (#include),
который переводится в incbin? или все
incbin разместить в стартапе?
Таблица релокации - для всех адресов,
т.е. чисел, которые вычисляются из адре─
сов? но $-label должно иметь размерность
числа! аlabel/256(в компиляторе нет)?
нужен флаг "адрес" в метке:"метка=число"
делает число,"метка"делает адрес,
"метка=метка+1"(просто по факту чтения
метки типа "адрес" или$в выражении)
делает адрес?
при всех арифметических операциях отслежи─
вать тип адрес/число?
как проще? или помечать выражения типа
"число"знаком#?
или помечать только выражения типа
$-label?
(в обычных программах не используются)
или разрешить$-labelтолько в специальной
команде определения метки?
Релоцируем не только переходы и вызовы,
но и вычисления адресаswitchи таблицыdw
Обнулять_islabelимеет смысл только в
командах,которые генерятся с записью слова
или пишут метку(label,=,dw,jp,call, ld)
Пока сделал так, что нельзя$-label
(можно1*($-label))
Как релоцировать обращения к другим мо─
дулям? Если как и внутри, то нельзя будет
распихать программу в память по частям...
26.06.2017:
Как передавать нумерацию модулей для
обращений в объектнике?
Можно ли относительный номер перенуме─
ровать в глобальный? (один и тот же модуль
может быть включен в разные!!!)
Номер можно назначать по порядку компи─
ляции (даже если перекомпилировать неизме─
нённый не надо)
Надо перекомпилировать неизменённый
модуль, если он использует изменённый мо─
дуль!!! (это потому что он привязывается к
смещениям в декларациях используемого мо─
дуля)
Даже если привязываться не к смещениям,
а к номерам символов, то вставка функции
потребует перекомпилировать все модули,
которые используют его.
Можно от первого изменившегося переком─
пилировать все последующие модули.
Ещё надо перекомпилировать всё при из─
менении структуры связей модулей.
Если это не сделать, то в линкере надо
работать с символьными метками, то есть
линкер не может стать лоадером (нужна па─
мять под метки)
Какой символ сделать для деклараций ти─
паmetka=<symbol><номермодуля>+<смещение>?
или другой формат деклараций?
Ассемблерный файл должен знать свой но─
мер модуля, чтобы правильно выгружать дек─
ларации!
Не страшно, если у ассемблера будет тот
же список файлов, что у компилятора?
Или список файлов компилятора ни на что
не влияет?
27.06.2017: добавил #include . До этого
все используемые внешние метки надо было
перечислять в начале исходника.
28.06.2017: добавил #define (только для
констант).
29.06.2017: разделил стандартную библи─
отеку на три -lib (умножения-деления,
сдвиги и т.п.),str (строки) и io (файлы).
Так можно сократить размеры утилит, кото─
рые не используют какую-то из частей.
30.06.2017: разбил исходник компилятора
на кодогенератор и парсер, они компилирую─
тся и ассемблируются отдельно (кодогенера─
тор под адрес 0x6003, парсер под адрес
0x6000 и включает бинарник кодогенерато─
ра). Для этого пришлось сделать экспорт
меток из ассемблера и директивыinclude,
incbin . Зато теперь появилась свободная
память под метки. Потом хотелось бы сде─
лать ещё и incbin с релокацией - данные
для релокации ассемблер уже выгружает.
Зарелизил эту версию официально.
Путь к многоплатформенности
03.07.2017: добавил типизированные кон─
станты:
#define <costname> (<type>)(<expr>)
04.07.2017: добавил #ifdef/ifndef (пока
невложенный).
Начал писать кодогенератор под ARM Thumb.
06.07.2017: добавил #undef .
07.07.2017:
Как делать макросы в ассемблере?
Вместоusemacro лучше # ?
Если поле метки=поле команды, то вообще
убрать нельзя, т.к. с точки зрения токени─
затора строка "macro1" - это объявление
метки,а строка"macro1 par1,par2" - ошибка
Или можно без#, если ловить макросы в
tokcalllbl вместо ошибки, но при этом
вставлять невидимый токенusemacro.
08.07.2017: добавил typedef. Кроме удо─
бства для пользователя, теперь можно будет
расширять систему типов и в самом языке -
сейчас-то код типа всегда лежит в перемен─
ных и параметрах типаBYTE. А будет лежать
в переменных и параметрах типаTYPE. Это
будет, например,UINT, примерно как в дре─
внем компиляторе Си (число звёздочек в
указателях при этом явно ограничено). Или
указатель на структуру, как в компиляторе
Оберона.
10.07.2017:
Класть макросы в буфер в памяти (отде─
льная ветка в чтении файла? дублирование
главного цикла? выделитьswitch в процеду─
ру? всё это медленно!), в отдельный файл
(сколько же будет открыто файлов?), патч
вызоваreadfin? (как это на языке высокого
уровня?) или делать отматывание файла? От─
матыванием можно ещё сделатьdup-edup. Но
пока нужных функцийseek/pos нет в файло─
вой библиотеке, даже нетflush без закры─
тия файла.
Переделал все скрипты.bat так, чтобы
они выглядели более-менее кроссплатформен─
но: состояли из строчек
<имяпрограммы> <параметры>
11.07.2017: добавил спрайтовую библио─
текуsprite.
Нужен нативный командный процессор.
Желательно, чтобы формат батника совпа─
дал с MS-DOS и UNIX.
Т.е. вызовы без расширений + имена фай─
лов без ключей, и больше ничего.
При работе командного процессора можно
хранить только резидент и открытый файл
батника, остальное каждый раз подгружать.
В .bat уже нельзя будет call comp.bat -
надо делать отдельные экзешники comp, tok,
asm.
C++ Builder не позволяет назвать проект
asm, поэтому команды будут nedolang,
nedotok, nedoasm.
Как компилировать во временной папке? и
даже чтобы исходники были разбросаны по
каталогам?
В MS-DOS и UNIX разные слеши, тогда ба─
тники с ними будут несовместимы. Поэтому
везде будет прямой слеш, он по стандарту
поддерживается в #include, а в утилитах
для MS-DOS я его перекодирую.
Можно сделать утилиту очистки временных
файлов - для всех трёх осей разную.
12.07.2017: ускорил вывод спрайта и до─
бавил ещё функции. Исправлял утилиту
nedores.
13.07.2017: разделил компилятор на два
проекта: nedolang и nedolarm, уже выдаётся
какой-то код под ARM Thumb.
14.07.2017: поддержал типы указателей
со звёздочками.
17.07.2017: тестировал спрайтовую биб─
лиотеку. Начал добавлять поддержку тайлов.
18.07.2017: по просьбе Hippiman'а доба─
вил в спрайтовую библиотеку вертикальную
прокрутку окна.
26.07.2017: для совместимости с Си все
обращения к структурным типам предваряются
словомstruct .
31.07.2017:
Мысли, как сдвигать по вертикали весь
активный экран:
- линейный экранный буфер? (на нём удо─
бнее линии и полигоны) 0x6000: startup,
ISR, music, data(?), scrbuf, gfx engine,
logic engine, gfx/logic/data (pageable) +
INT vector
- todo отрисовка элементов панельки и
диалогов
- todo возможность выводить спрайты по─
верх панельки??? фреймово прямо на экране,
как стрелка в ЧВ?
- todo клипировать спрайты внутри акти─
вного экрана? ? только с переброской? ?
(128K экран вряд ли будет эффективным -
мало места под графику в нижней памяти)
03.08.2017: вместо 196 файлов в кучу
разложил по разным каталогам сам компиля─
тор с библиотеками (SDK) и проекты на
NedoLang (у каждого свой каталог). Для
этого поддержал пути везде, где надо (в
MS-DOS - перекодирование из прямых слэшей
в обрантые, а на Speccy под TR-DOS - про─
пуск до последнего слеша).
Оптимизировал== и != , как задумывал
раньше.
04.08.2017: написал нативную утилиту
batch, которая исполняет те самые кросс─
платформенные батники компиляции. Лежит с
адреса64000, так что если раньше было те─
сно, то теперь очень тесно (реально я без
остановки занимался оптимизацией компиля─
тора по размеру и меткам, чтобы в конце
концов втиснуть batch ).Зато теперь не ну─
жно писать хитрые бейсики, в которых не
хватает места (их однажды даже пришлось
разрезать пополам), можно сделать сложную
автосборку. Утилита batch имеет свою копию
файловой библиотеки, общие с компилятором
только дисковые буфера (они на экране -
одновременно открыто до8 файлов). Вот ес─
ли бы была ОС, то файловая система была бы
только в одном экземпляре.
07.08.2017: добавил библиотекуprint, а
с её помощью утилиту diff, которая показы─
вает различие файлов на экране. Теперь мо─
жно тестировать систему автоматически, без
ручного копирования-сравнения.
08.08.2017: добавил утилиты nedodel
(удаление файла) и movedisk (уплотнение
диска), потому что проект компилятора не
помещался на дискете.
10-15.08.2017: переводил библиотеку io
с NedoAsm на NedoLang, чтобы можно было её
использовать на ARM. Строковую библиотеку
нарезал из процедур, которые уже давно бы─
ли сделаны для компиляции из-под C++
Builder. В это времяHippiman смог вывести
на экран карту, редактируемую в DizzyAGE,
и подвигать по ней Диззи :)
17.08.2017: багфикс пересекающихся наз─
ваний параметров и локальных переменных в
разных функциях. Это я так экономил память
под метки в компиляторе.
18.08.2017: фиксил кодогенератор под
ARM Thumb. Оказалось, что там и метки, и
всяческие defb'ы принято писать в другом
формате, причём в разных ассемблерах
по-разному. Я выбрал Keil, потому что
Phyton не ест числа видаOxff. А компиля─
тор пока не умеет перекодировать константы
- передаёт как есть.
19.08.2017: багфикс импорта атрибутов
тайлов в nedores. Было заметно только, ес─
ли самому рисовать в атрибутах или выво─
дить спрайты поверх тайлов.
21.08.2017: мой выдаваемый ассемблерный
код под ARM Thumb наконец стал ассемблиро─
ваться.
22.08.2017: токенизатор скомпилировался
под ARM Thumb. Но пока не определены неко─
торые функции.
23.08.2017: добавил недостающие функ─
ции.Токенизатор слинковался под ARM Thumb.
Начал отладку в Phyton. Пришлось хакнуть
описание микроконтроллера 198бВЕ1Т в комп─
лекте Phyton, чтобы с адреса0 было боль─
шое-большое ОЗУ.
24.08.2017: добавил утилиту nedopad
(урезание или расширение файла до заданно─
го размера) для удобства загрузки програм─
мы и образа диска в симулятор Phyton.
25.08.2017: токенизатор, скомпилирован─
ный под ARM Thumb, работает под симулято─
ром! Результат токенизации файла на образе
диска соответствует образцу.
Начал писать свой ассемблер под ARM
Thumb вместо Keil 'овского (точнее, делать
ветку NedoAsm с изменениями в главном
switch и некоторых процедурах, а остальные
части общие).
27.08.2017: багфикс атрибутов пустых
знакомест в nedores. Добавил библиотеку
звуковых эффектов ayfxplay из игры Ball
Quest и Evo SDK.
28.08.2017: начал параллельно писать
токенизатор под ARM Thumb (точнее, опять
делать ветку с общими частями).
29.08.2017: токенизатор и ассемблер под
ARM Thumb работают! Была некоторая пробле─
ма с генерацией меток процедур со смещени─
ем+1 . Не смог сделать так же, как в Keil
(как он узнаёт, что метка относится к про─
цедуре, а не к переменной?). Сделал несов─
местимо. Но он мне больше не нужен. Я про─
сто сравнил результат побайтно.
Ещё добавил рантайм библиотеку для128K
(прерывания, странички, бордер).
Зарелизил эту версию.
Гладко было на бумаге...
Я взял фирменную отладочную плату мик─
роконтроллера 198бВЕ1Т фирмы "Миландр". С
кучей перемычек от прошлого хозяина, кото─
рые были выставлены явно не так, как нужно
было мне. После некоторых копаний я нашёл
нужное положение перемычек и одну програм─
му, которая скомпилировалась в Phyton.
Оказалось, что реальная железка неско─
лько отличается по поведению от отладчика.
Речь не только о том, что с адреса0 нет
ОЗУ, там вообще только48K ОЗУ (причём не
подряд),из которых под код можно использо─
вать только16K. Поэтому вместо токениза─
тора я отлаживал просто копировщик файла.
Более того, при попытке загрузить код в
ОЗУ Phyton сбрасывал систему, так что я
терял все многочисленные настройки регист─
ров процессора, которые для меня делала
программа-образец. Так что я сделал свой
загрузчик по Ethernet (сервер в ПЗУ и кли─
ент отдельно). Он поддерживает только две
команды по UDP: записать байты в память
туда-то, запустить с заданного адреса.
В некоторых случаях библиотека io обра─
щалась к невыровненным данным. Я наивно
использовал структуру дескриптора из
TR-DOS прямо в сыром виде. Но на реальной
железке, в отличие от симулятора, невыров─
ненный доступ вызывал прерывание, которое
было довольно сложно отследить. Файла
трассировки, опять-таки, в отличие от си─
мулятора, уже не было, поэтому я медленно
проходил код процедура за процедурой. То─
чек останова в ОЗУ тоже не было, поэтому я
вручную ставилB {PC} (аналог jr $ ),а по─
том его убирал.
В конце концов12.09.2017 всё заработа─
ло. Я ещё поэкспериментировал с внешним
ОЗУ (оказалось, были перепутаны провода
выборки байтов,но в общем работать можно),
лампочками и кнопочками, потом отложил
плату.
Следующий этап - микроконтроллер "Муль─
тикор" фирмы Элвис.
А также - как поддержать банкинг, как
ещё приблизить к Си и как прикрутить тот
самый 3D движок. И написать специализиро─
ванную среду разработки. И операционную
систему под неё :)
07.09.2017 ещё добавил в спрайтовый
движок работу с пикселями на экране и в
экранном буфере, 09.09.2017 по просьбе
Hippiman'а переделал спрайтовый движок на
постоянную ширину тайлсета (28 знакомест,
чтобы уместились атрибуты),13.09.2017 со─
кратил архив на 259 файлов (добавил утили─
ту clean), а15.09.2017 описал в красках,
как делатьHello world. Проверка на соседе
показала, что инструкция работает :)
* * *
За год в рамках проекта написано20000
строк кода (550килобайт) и под200кило─
байт текстов, не считая этой статьи (а в
этой статье ещё80, приятного аппетита!).
В среднем60отлаженных строк кода в сутки
(с некоторыми отвлечениями на другие рабо─
ты), что чуть выше индустриального станда─
рта :)
Other articles: