Rev 120 | Blame | Compare with Previous | Last modification | View Log | Download
а) с линкером:# сделать экспорт деклараций из асма (и предусмотреть токен?) СДЕЛАНО# сделать EXPORT в компиляторе СДЕЛАНО# сделать ассемблирование цепочки файлов - СДЕЛАН INCLUDE (теперь надо сделать #include в компиляторе)# сделать экспорт объектника в асме (патчи на месте и на будущее патчи назад)# сделать линкер- после этого можно развивать компилятор и асм#б) без линкера:# сделать экспорт деклараций из асма (и предусмотреть токен?) СДЕЛАНО# сделать EXPORT в компиляторе - для процедур, функций, переменных, констант, константных строк/массивов/структур СДЕЛАНО# сделать ассемблирование цепочки файлов - СДЕЛАН INCLUDE (теперь надо сделать #include в компиляторе)# сделать подклеивание бинарников с меткой (вместо nedodefb и для подключения модулей) СДЕЛАНО# сделать экспорт таблицы релокации (или обжа?) СДЕЛАНО# сделать подклеивание бинарников с релокацией (или обжей?)- после этого можно развивать компилятор и асмкак отличать в команде, где асм, где бинарник? или реализовать incbin (он невложенный)? тогда сделать USEUNIT (#include), который переводится в incbin? или все incbin разместить в стартапе?надо типизировать oldvalue и стек pushvalue, тогда можно _isaddr сбрасывать при вычитании двух адресов (сейчас приходится юзать 1*(addr-addr))вместо usemacro лучше #?если поле метки=поле команды, то вообще убрать нельзя, т.к. с точки зрения токенизатора строка "macro1" - это объявление метки, а строка "macro1 par1,par2" - ошибкаили можно без #, если ловить макросы в tokcalllbl вместо ошибки, но вставлять невидимый токен usemacroкак вычитывать макросы в asmtg:а) отдельный цикл с тем же switch - жирноб) не читать непосредственно readfin, а через процедуру - замедлит, или процедура на асме? какая? (идея была читать всегда из 256-байтного буфера в памяти, но как это сделать на ЯВУ? вместо вызова должен был быть макрос)в) генерировать файлы и читать ихг) switch выделить в процедуру - однозначно замедлитд) патч вызова readfin?е)таблица релокации - для всех адресов, т.е. чисел, которые вычисляются из адресов? но $-label должно иметь размерность числа! а label/256 (в компиляторе нет)?нужен флаг "адрес" в метке ("метка=число" делает число, "метка" делает адрес, "метка=метка+1" (просто по факту чтения метки типа "адрес" или $ в выражении) делает адрес)при всех арифметических операциях отслеживать тип адрес/число?как проще? или помечать выражения типа "число" знаком #?или помечать только выражения типа $-label? (в обычных программах не используются)или разрешить $-label только в специальной команде определения метки?релоцируем не только переходы и вызовы, но и вычисления адреса switch и таблицы dwобнулять _islabel имеет смысл только в командах, которые генерятся с записью слова или пишут метку (label, =, dw, jp, call, ld)пока сделал так, что нельзя $-label (можно 1*($-label))пока просто выгружаем адреса патчей в forg (вместо размеров блоков)как строить проект компилятора?нельзя взаимные ссылки - допустим, вынесем в модульодин модуль может быть включён в разные модули - как избежать дублирования кода? emit используется почти везде, т.к. он выводит ошибкидаже если разрешить дублировать ошибки, то asmstr есть в compile (asm, switch, case, константные строки), commands (можно убрать), codetgвыигрыш по меткам будет только при инкобже какой-то терминальной ветви включения модулей, с кучей метоктаких в компиляторе нет!как релоцировать обращения к другим модулям? (если как и внутри, то ещё и нельзя будет распихать программу в память по частям!)надо чтобы при компиляции модуля был известен номер блока (глобальный для проекта!) и смещение каждого обращения наружуглобальный номер блока можно взять только из комстроки компилятора, он должен попасть в декларацию (в каком виде?)как сэкономить в ассемблере память на локальных переменных? (надо не более 55к для самокомпиляции, а лучше 48 для CP/M - вроде уже 32к, но надо добавить токенизацию (выражения только с >>16 и $+num, КРОМЕ АСМОВСТАВОК!!!), буферизацию)- линковать (в идеале вложенно) (в идеале с взаимозависимостями)- сбрасывать посты после каждой функции, переменные прилеплять в хвост функции. тогда можно и локальное пространство имён для небольшого сокращения данных (но увеличения кода)чтобы можно было удалять локальные метки функции (те, к которым обращение только назад и не было обращения вперёд), достаточно помнить массив адресов хэшей. Запомнить его надо после объявления заголовка функции. в модуле так не получится, т.к. там объявляются ещё экспортируемые метки, которые удалять нельзя.локальные переменные функции придётся компилировать в едином блоке с функцией, иначе нельзя будет их удалять.- на втором проходе оставить в таблице только метки, к которым были обращения до присваивания, остальные стереть (или две разных таблицы? тогда на первом проходе не присваивать такие метки, даже если они потом используются после определения)- вместо имён CRC32- СДЕЛАНО: сократить названия процедур и переменных в компиляторе- СДЕЛАНО: сократить число параметров и локальных переменных в компиляторе- СДЕЛАНО: сократить число процедур в компиляторе- СДЕЛАНО: односимвольные автометки (с префиксом функции)- заменить компилятором в процедурах локальные переменные на автометки без префикса (но тогда будет конфликт в разных модулях!)генерировать user.l для отладки в анрилеможно разделить асм;1. обработка мнемоник и генерация кода + объявлений + полей (выражений)2. линковка кода + объявлений + полей, но там все существующие метки!можно для второго прохода подготовить другой асмотекст, где половина скомпилирована в db и без части меток (к которым не было обращения вперёд)но так всё равно в первом проходе надо пройти все существующие метки!и писать-читать такой объём данных будет очень медленнометки в компиляторе:1) глобальные константы - короткое имя2) глобальные переменные - короткое имя3) внешние метки параметров -4) локальные метки параметров - МОЖНО УДАЛЯТЬ5) точки входа в функции - короткое имя6) переходы назад - МОЖНО УДАЛЯТЬ, генерировать $-...7) переходы вперёд -[8) локальные константы - МОЖНО С КОРОТКИМ ИМЕНЕМ, МОЖНО УДАЛЯТЬ]9) локальные переменные - МОЖНО С КОРОТКИМ ИМЕНЕМ, МОЖНО УДАЛЯТЬможно сделать make независимым от редакторадля этогов редакторе memory mapped file, при любой записи в любой файл в директории АТОМАРНО С ЭТИМ появляется файл "обновлено"при любом чтении проверяется наличие файла "обновлено" - если он есть, то чтение прерывается с ошибкой "устарело"ошибка прерывает и перезапускает всю цепочку компиляции, а также запуск результатаmake вызывается из главного цикла ожидания "обновлено" (раз в секунду?)перекомпилируем только изменённые файлы - как узнать? время файла ненадёжно, можно тикать номером версии файла в параметре start? а в FAT тикать секундами???obj реально лежат в файловой системе, тоже с уникальным номером версии/временем (что делать при переполнении? смотреть знак разницы номера?)в редакторе кнопка "запустить" обновляет файл проекта, добавляет туда флаг "с запуском". при чтении файла проекта этот флаг снимается - конфликт!!!надо отслеживать текущий номер запуска и обновлять его? при первой сборке (при старте редактора) перед входом в цикл смотреть текущий номер запуска, чтобы не запустить?как обновлять неатомарно?если поздно создать файл "обновлено", то make может докомпилировать и запустить старую версию проекта, несмотря на то, что исправили и снова нажали "запустить"но это возможно и при чтении блоками - во время обработки последнего блока появится файл "обновлено", но мы его уже не увидимтак что лучше просто не нажимать два раза "запустить"!?!настоящая проблема - если изменить текст файла, а файл "обновлено" не успеет создаться, тогда будет ошибка компиляции или неожиданный результат (частичное обновление исходника). то же при слишком раннем создании файла "обновлено"послать сигнал не получится - каждый раз работает неизвестная утилитав своей фс мы ещё как-то можем сделать атомарное созданиеНО КАК БЫТЬ ПРИ FAT?нам надо разбить текст на полуфайлы по курсоруstdio должен уметь находить оба полуфайла как один файл и читать его полностью через все блокикак сделать добавление в начало второго полуфайла? и чтобы работало в FAT?при удалении начала второго полуфайла могут образоваться пустые блоки, но удалять их нельзя, т.к. идёт чтение!у второго полуфайла надо знать номер первого блока - где его класть? или сделать полный набор пустых блоков в начале на всякий случай?тогда блоки у каждого полуфайла нумеруются с 0где хранить номер блока файла?start/время? тело файла? последние 2 символа расширения?где хранить начало данных внутри блока? при удалении данных из начала мы корректируем именно это число!!! start занятвперёд смотрят метки if, while (выход), repeat (выход) а также forward процедур - их оставить линкеру?назад смотрят метки while (цикл), repeat (цикл), par (т.к. можно рассчитать), var (т.к. можно рассчитать)в каком виде чередовать код и команды линкеру?1а) код блоками максимум 256 байт (размер буфера) с заголовком, где длина1б) данные так же - в отдельном файле?определения декларируемых меток передаются линкеру в виде:2) локальная метка перехода (или инлайн var/par): имя + смещение в текущем сегменте3) экспортируемая метка перехода (или экспортируемый инлайн par): имя + смещение в текущем сегменте4) экспортируемая метка var/par: имя + смещение5) экспортируемая константа: имя + значениенерассчитанные обращения к меткам (выражение (для >>) с именами и рассчитанными обращениями - распарсено с явными push-pop?) передаются линкеру для заполнения в таких форматах:6) byte (младший)7) uint (младший)8) long (для DL)[...) uint (старший)][...) byte (>>8), byte (>>16), byte (>>24)][...) смещение для jr??? нереально угадать jr/jp вперёд, а назад уже рассчитано]рассчитанные обращения к меткам передаются линкеру в виде:A) "текущий код" + смещение (uint)Б) "текущие данные" + смещение (uint)В) "long константа" + данные (long)формат заголовка блока:+0 1 ⨯:0"конец"1"код"/"данные" + длина + байты2"объявление локальной метки перехода" + имя + смещение (в текущем сегменте кода)3"объявление экспортируемой метки перехода" + имя + смещение (в текущем сегменте кода)4"объявление экспортируемой метки var/par" + имя + смещение (в текущем сегменте var)5"объявление экспортируемой константы" + имя + значение6"выражение byte"7"выражение uint"8"выражение long"если компилятор будет чередовать в одном файле блоки кода и данных, то линкер не сможет их раскидать в один проходприцеплять данные строго в конец файла (надо огромный буфер в компиляторе) - поможет линкеру только если разрешить чередование кода и данных в бинарникеесли два файла, то как линкер поймёт, какой сегмент - "текущие данные"? заранее знать число блоков кода и прибавлять его?чтобы линкер мог не подцепить ненужную процедуру, она должна быть в отдельном файле. но номер этого файла надо тогда учесть в общем счёте!как узнать, что не надо подцеплять? а вдруг она используется ниже? или все либы в конце в иерархическом порядке - тогда не надо подцеплять либу, если ни одна декларация из либы не использована (кроме локальных меток перехода)объявления надо в отдельных файлах, иначе не получится в один проход!!!порядок подачи obj (startup рассматривается как обычный код, обжи умеют выгружаться и из асма, и впоследствии из компилятора):decl1 + decl2 + libdecl1 + libdecl2 +code1 + code2 + lib1 + lib2 +var1 + var2 + libvar1 + libvar2блок неинициализированных переменных не нужен - это решается пакером?файл декларации закрывается только после окончания компиляции соответствующего файла, т.е. обжи code и var от него должны буферизироваться (в памяти или в файле)более того, поскольку сначала мы читаем ВСЕ файлы декларации, то буферизируются ВСЕ обжи code и var!!! для компилятора их объём будет порядка 48К (весь код с данными (32К) + половина меток (16К) + заголовки (16К?))как сэкономить хоть сколько-то буферной памяти?это можно только при чередовании кода и данных, т.к. само разделение code и var требует буферизировать целиком оба, прежде чем можно начать склеиватьили склеить в два бинарника?но всё равно - как быть с декларациями? как-то выгружать файл патчей, который будет использовать лоадер?пусть мы чередуем вызовы компилятора и асма, а асм выгружает обжи и декларации (label1=M1+123 label2=M1+456 и т.д. - лучше в токенизированном виде, т.к. это личное дело асма)(причём выгружает столько копий деклараций, сколько нужно?)взаимозависимость модулей запрещена - только зависимость главного от нижних (ТОГДА ВМЕСТО ЛИНКЕРА МОЖНО include "mod1.dec":incbin "mod1.bin")тогда не требуется память одновременно под исходник, асм, токенизированный асм и обж, а для каждого требуется максимум память под обж и декларацииитого максимальная память требуется, когда скомпилированы все модули, кроме главного:вся система займёт в памяти как минимум:32K редактор (как ACEdit)32K текущий редактируемый исходник (может быть не один! весь проект компилятора 210K)24K компилятор8K метки компилятора [(+8K метки перехода?)][10K токенизатор ассемблера в компиляторе или больше отдельно (можно убрать, если редактируем ассемблер уже токенизированный, а асмовставок нет)]10K ассемблер16K метки ассемблера (вдвое меньше, чем сейчас - под один модуль + использумые декларации)4K линкер[16K метки линкера (все экспортируемые + метки перехода вперёд)]40K выходной бинарник (по размеру самой большой программы с данными: редактор с маленьким текстом или компилятор с метками)48K буферизация всех обжей проекта8K экран, системные переменные, стек4K ось (память, ввод-вывод, шедулер, ФС, лоадер)[16K файловые буфера (надо на все обжи одновременно! или уметь замораживать незахваченный пайп без буфера?)]---~240K (будет больше из-за многотекстовости - до 454K, не считая запасов под хвосты текстов)как при этих лишних файлах не потерять скорость? делать однопроходный асм с патчами в обже (линкер/лоадер применит патчи)?как передать длинный мейкфайл типа такого?//при изменении mod1.c или mod1.hcompile mod1.h mod1.ctokenize mod1.asm mod1.varassemble mod1.tok //делает mod1.obj, mod1.dec//при изменении asm1.asmtokenize asm1.asmassemble asm1.tok //делает asm1.obj, asm1.dec//при изменении main.c или mod1.h или (как отследить?) деклараций модулейcompile mod1.h main.c //тот же хедер, что вышеtokenize mod1.dec asm1.dec main.asm main.varassemble main.tok //делает main.obj//при изменении любого файлаlink mod1.obj asm1.obj main1.objили без линкера (та же скорость и на один вызов меньше)://при изменении mod1.c или mod1.hcompile mod1.h mod1.ctokenize mod1.asm mod1.varassemble mod1.tok //делает mod1.bin, mod1.dec//при изменении asm1.asmtokenize asm1.asmassemble asm1.tok //делает asm1.bin, asm1.dec//при изменении любого файлаcompile mod1.h main.c //тот же хедер, что вышеtokenize main.asm main.varassemble main.tok mod1.dec mod1.bin asm1.dec asm1.bin //делает main.binсейчас сравнения < <= > >= делаются в UINT, а надо в LONG (пока не поддержано в компиляторе)надо такой порядок переменных, где не понадобятся post labels?т.е. нужны предописания процедур, но как их компилить? даже джамп вперёд не получитсяа что делать с if, while, где тоже джамп вперёд? компилить тело, потом патчить? (так делает Turbo Pascal) это не вывод в последовательный файл!это допустимо для hex-файла? только с пропуском байт, чтобы не писать 2 раза в одно место?или генерировать процедуру целиком в массив, потом скидывать в поток?или генерировать патчи в последовательный файл objкак делать dup-edup?а) буферизировать текст тела (как макрос)б) прогонять файл несколько раз от середины (так нельзя стримить)в) буферизировать выходной бинарник (так нельзя переприсваивать метки внутри dup и делать разное наполнение проходов)как делать постметки?а) запоминать всю строку с выражением (т.е. все строки надо считывать целиком вне зависимости от выражения)б) два прохода - на первом смотрим длины команд и генерим обычные (не пост) метки, на втором генерим оставшиеся метки и пишем кодв) разрешить вперёд обращаться только к метке (без выражения)т.е. если метка не определена, то проверяем, что это конец выражения, иначе ошибкаа как получить адрес, куда писать? на момент разбора выражения он неизвестен! добавить операции записи в середину команды?г) при неопределённой метке сохранить в пост всё состояние калькулятора и остаток выраженияа как получить адрес, куда писать? на момент разбора выражения он неизвестен! добавить операции записи в середину команды?бывают однобайтные и двухбайтные постыдвухбайтные могут быть только curaddr (только для dw) и curaddr+1(curaddr+2 только для ix,iy - но как пост узнает, что такая команда? по регистрам не узнает!)однобайтные бывают:- db (curaddr)- jr (curaddr+1)- смещения для ix,iy (curaddr+2)- числа (обычно curaddr+1, но для h/lx/y curaddr+2, для ld (ix/y+d),N curaddr+3 - как пост узнает тип команды? по регистрам не узнает!)или можно сохранить всё состояние ассемблера и строку до конца, а посты компилировать так же, как обычный токенизированный текстно для того, чтобы пропустить команду в первом проходе, надо её всю интерпретировать(как минимум прочитать регистры и огранизовать проверку формата с учётом регистров)тогда можно огранизовать пропуск в asmbyte, asmword итпа) скопировать код всех форматов и регистровб) поставить ловушку в readfin()? будет замедление! там предполагался только системный макрос!в) без постов. два прохода, на первом команда не пишетсяг) без постов. бесконечное число первых проходов (где команда не пишется) - пока не определены все метки. на втором проходе метки не определятьд) без постов. два прохода, на первом команда тоже пишется - т.е. одинаковые проходы с общей таблицей меток, но надо по-разному обрабатывать display и ошибку переопределения меткитак можно бесконечное число проходов - пока не определены все метки или не перестало изменяться число неопределённых меток.так нельзя генерить в hex, т.к. повторы записи в одно и то же место.решается либо моделью памяти, либо игнорированием бинарника, полученного на предыдущем проходе.а где генерировать ошибки "метка не определена"? надо в конце отдельный обход таблицы меток для этого. но в метке не хранится номер строки!е) исходник в памяти, всегда можно вернуться на начало строки.ё) предварительное чтение строки в память. Будет замедление.ж) отматывать указатель файла (как это будет в оси? пайп так нельзя).з) добавить операции записи в середину команды. при этом ld hx,expr надо писать код ld hx ДО ВЫЧИСЛЕНИЯ (он определяет смещение данных, а регистр hx может быть сам из метки). в токенизаторе придётся читать строку целиком?