Rev 653 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download
почему из Evo Reset Service глючит asm/run.bat? (на этапе ассемблирования)
в Mr Gluk Reset Service работает!
при самокомпиляции компилятора примерно 4-6 млн тактов между чтениями сектора 256 байт
- быстрое чтение символа лексером (rdaddword) = 330t (т.е. в сумме 84480 тактов, т.е. <2%) - это с контролем длины принятого слова _tword, а само чтение = 84t
- среднее время между вызовами rdaddword ~ 6000t (как будто в среднем 18 символов в слове, но реально в среднем 2.6 символов в слове)
- среднее время исполнения rdaddword ~ 2000t (из них ~ 1000t в главном цикле), т.е. остаток компилятора ~ 4000t. В 50% случаев слово из 1 символа = 1021 t (614/884/1021 после замены порядка проверок - время 114.88 на эмуляторе 240000t, было 123.12).
- 112.58 при сравнении _lentword байтово.
- 111.56 при проверке в val в порядке '~', >=0x41, всё остальное
- 109.50 чистка комментов read.c
- 109.38 чистка комментов compile.c
- 105.82 (сам компилятор из них 33.20, т.е. выигранные 17.30 - существенный процент) переставил условия в eatcmd
- (67.58 без учёта nedoasm, т.е. асм 38 секунд)
- 109.06 ускорение movedisk (добавлена математика, убраны лишние чтения-записи - а они не шли в счёт времени)
- 111.94 добавил rrca в hash
- 113.86 добавил второй rrca в hash
- 108.98 убрал оба rrca и заодно xor l в hash
- 105.28 ассемблерный findlabel в nedoasm
- 104.02 ассемблерный lbltype в nedolang
- 103.04 ассемблерный gettypename в nedolang (_typeaddr в C индекс, в асме указатель (лезет в UINT))
- 102.70 ассемблерный addlbl в nedolang
- 102.94 ассемблерный initlblbuf в nedolang (везде адреса меток, а не индексы), убрано поле длины метки
- 102.80 возвращён xor l в hash внутри ассемблерных процедур nedolang
как форвардить переменные: игнорировать одинаковые - совместимо с Си
как форвардить константы??? брать последнюю (можно и как метку, и как переменную). не сработает, если ассемблируем по частям, сработает, если ассемблируем всё в кучу
как форвардить процедуры без ключевого слова? отложить генерацию полей до самого конца заголовка? надо помнить весь заголовок в разобранном виде, т.к. лексить из строки не получится!!!
ассемблер должен склеить:
1. модуль неявных математических функций (компилятору не нужно, но кодогенератор подклеивает нужный? или скрипт компиляции меняется в зависимости от таргета?) - подключать в конце, чтобы были только используемые процедуры
2. заголовочный файл ОС (компилятору не нужно, про этот файл знает только скрипт компиляции под данный таргет!!!)
3. модуль запуска? или два - начало и конец? (компилятору не нужно, то же!!!)
4. код + код + код...
5. переменные + переменные + переменные...
6. строки + строки + строки...
можно в недоасме исключить неиспользуемые процедуры на втором проходе?
надо какой-то хитрый иф, который проверяет, есть ли процедура в метках первого прохода на втором проходе.
(ifused для функций библиотек в ZXASM)
но тогда не получится исключить процедуру, которая используется только в неиспользованной процедуре
(пока что в недоасме нет даже простой условной компиляции с проверкой существования метки)
в компиляторе
чтобы не было конфликта пространств имён со структурами, писать пространства имён через '@' вместо '.'?
или на каждом слове проверять, не структура ли? это тормоз
какой префикс сделать для регистровых подсказок компилятору?
+(regfast1 type)ccc
+REGFAST1 ccc
+REGFAST1(ccc)
регистровые подсказки в левой части присваивания хотелось бы такие же, а там тайпкаст (скобки) нельзя
+ там тоже нельзя!!! то есть не отличить от обычной переменной!!!
тогда надо подсказку (без плюса) писать перед LET
или подсказку хранить в типе (сильно ограниченное число подсказок)
чаще всего регистры нужны в inc, dec, peek, poke, передаче параметров
#const и #eval надо препроцессором
#include тоже
#if (только по константному флагу) тоже
define(<command>[<text>]) должен генерировать определения, которые подставляются в текст с помощью префикса на уровне readcommand (чтобы не искать совпадения на каждом слове)
в отличие от сишного препроцессора, надо сделать препроцессор не построчный, а терпимый к переводам строки (например, все параметры в скобках)
так можно включить препроцессор в основной цикл парсера
include?("filename.ned")
const?(a=[const])
ifconst?(a)command~command
если компилятор пишем на его же языке:
как реализовать эффективный подсос из указателя и выплёвывание в указатель? асмовставками?
или резервировать регистры под cin и cout на уровне языка? например, hl', de'
чтобы работать с большим числом файлов, передавать файлы в процедуры как потоки?
нам нужно вход, выход, поток ошибок, лог? - доступные везде?
в турбопаскале разбор ключевых слов идёт так:
GetTYPE:
call GetSimpTYPE ; Test simple type
ret z
call FindStr ; Skip PACKED
dw $PACKED
call ..ARRAY ; Check ARRAY
ret z
call ..RECORD ; Check RECORD
ret z
call ..SET ; Check SET
ret z
call ..PTR ; Check ^
ret z
call ..FILE ; Check FILE
ret z
call ..STRING ; Check STRING
ret z
call ..SCALARE ; Test SCALARE ()
ret z
call ..RANGE ; Test RANGE ..
ret z
call ERROR ; Type declaration expected
db _TypeExp
...
..ARRAY:
call FindStr ; Check ARRAY
dw $ARRAY
ret nz ; .. nope
call MustOpen ; Verify [
...
то есть идёт прямая проверка всех строк, причём на глубоком уровне вызова и с выходами из середины
<var>[<expr>]
компилировать в последовательность:
cemitpushnum; //вместо pushvar
read_compile_expression; //на выходе из expression уже прочитана ')', но следующий символ или команда не прочитаны
readcommand; //чтобы иметь symbol на выходе value
cemitarrayshift(typesize); //!!! компилятор не знает размер типа, его может прочитать commands из asm
cemitadd;
cemitpeek;
все размеры массива округлять до 2^n?
мы не можем это делать без вычисления констант!
с двумерным массивом:
<var>[<expr>][<expr>]
компилировать в последовательность:
cemitpushnum; //вместо pushvar
read_compile_expression; //на выходе из expression уже прочитана ')', но следующий символ или команда не прочитаны
readcommand; //чтобы иметь symbol на выходе value
cemitarrayshift(typesize); //!!! компилятор не знает размер типа, его может прочитать commands из asm
cemitadd;
readcommand; //skip '('
read_compile_expression; //на выходе из expression уже прочитана ')', но следующий символ или команда не прочитаны
readcommand; //чтобы иметь symbol на выходе value
cemitarrayshift(typesize+arraysize); //!!! компилятор не знает размер типа, его может прочитать commands из asm
cemitadd;
cemitpeek;
или:
cemitpushnum; //вместо pushvar
read_compile_expression; //на выходе из expression уже прочитана ')', но следующий символ или команда не прочитаны
readcommand; //чтобы иметь symbol на выходе value
readcommand; //skip '('
read_compile_expression; //на выходе из expression уже прочитана ')', но следующий символ или команда не прочитаны
readcommand; //чтобы иметь symbol на выходе value
cemitarrayshift(arraysize); //!!! компилятор пока не знает размер массива!!! (он передаётся как строковая константа [const])
cemitadd;
cemitarrayshift(typesize); //!!! компилятор не знает размер типа, его может прочитать commands из asm
cemitadd;
cemitpeek;
сделать [const] как расчёт константы из чисел и констант?
отдельной группой expression или попробовать засунуть в той же? (в каждой операции ветка по спецтипу, а в value читать числа в другом режиме, чтобы был спецтип и значение)
сейчас группа expression возвращает тип, а надо возвращать значение! какого типа? надо и то, и другое, т.е. запись!!!
а в ассемблере надо, чтобы expression мог генерить post label?
могут потребоваться и fixed point типы с особой операцией умножения (сложение и сравнение такие же)
префиксы могут совпадать с операциями и инфиксами
постфиксы не могут, т.к. они в позиции операции
+ префикс(int), операция
- префикс(int), операция
* префикс(peek), операция
/ операция
~ префикс(inv)
! префикс(invbool)
& префикс(addr), операция(and)
| операция(or)
^ операция(xor) - можно inv сюда как префикс
= операция(eq)
! операция(noteq)
< префикс(shl), операция(less)
> префикс(shr), операция(more)
. префикс(уровень выше), инфикс(float, record)
% префикс?, операция?
( префикс(expr)
[ префикс(const), постфикс(peekarray) - можно peek сюда как префикс [addr]? редко надо, можно тайпкастить указатель из числа: *(byte*)addr
@ префикс?, операция?
? префикс?
when defining a function, parameter order is: inputs, then outputs (Google cpp style guide)
по венгерской нотации первые буквы функции должны соответствовать типу результата
var, .var, var.var - нельзя отделять по isalphanumeric(nextsymbol) от type func, если нет точки с запятой!!!
type(var) - по nextsymbol=='('
[+type func(...)]
type func(...), если обязательная ';' (при этом нельзя type .func(...))
или так:
value.value.value
value.value.value[expr]
+(type)value.value.value - сделать как было, без pretype
+(type)value.value.value[expr] - сделать как было, без pretype (у [] приоритет над типизацией и всеми префиксами)
func.func.func(par,par,par) отделено уже внутри concat_compile_var() (вызов функции там и опознаётся именно по скобке справа)
!!!но как узнать про скобку, если ещё не прочитан полностью идентификатор со всеми точками??? решено разбивкой на части compile_variable
(type)func.func.func(par,par,par) в позиции команды нельзя, если поддержано (type)pointer=...
сделать poke(type)pointer=...?
или разрешить писать в указатель без тайпкаста?
для этого надо хранить тип функции и всех параметров (как переменных, которые не надо генерировать в блок переменных)
при этом все используемые функции надо определить выше!!!
forward сейчас пропускает, а должен создавать переменные! тогда в определении функции надо проверять переменные перед созданием!
слово forward нельзя пропускать, т.к. cemitfunc(); надо вызвать/не вызвать перед чтением параметров (т.к. они должны сохраняться в стек для рекурсии).
нельзя начинать команду со скобки, т.к. скобка приклеится к прошлому выражению (превратит последнюю переменную в функцию) - или проверить пробел между ними?
poke по * без let неудобно - может быть воспринято как продолжение прошлого выражения!!!
++var и --var тоже склеиваются с предыдущей строкой
поэтому inc var, dec var
можно сделать вызов без call, если проверять скобку ( после команды - но тогда нельзя имена с точкой
и присваивание (не массивов) без let, если проверять = после команды - но тогда нельзя имена с точкой
присваивание массивов без let, если проверять [ после команды - но тогда нельзя имена с точкой
подцеплять точки уже на уровне reads (кроме начальных)?
сейчас при объявлении функции с точкой вложенность не будет соответствовать числу точек!!!
нужно ли объявлять функции с точкой? вроде нет
сделать защиту от этого?
по идее правомерны только такие обращения с точками:
_proc()
_module.proc()
submodule.proc() (раньше было .submodule.proc())
_var
_module.var
[submodule.var (раньше было .submodule.var()) - плохой стиль]
.thismodulevar - как сделать совместимо с Си? сделать _var уровнем выше, а __var глобальным? [или все глобальные переменные модуля сделать глобальными (плохой стиль)? или работать с ним через _mymodule.var (а откуда мы знаем вложенность)?]
endif для условий без else ликвидирует ошибку if()if(){}else{}
если есть endif, то ошибки if();{} не может быть, так что можно убрать then
но loop просто так не убрать
можно сделать обязательную фигурную скобку после if() (тогда будет неудобно if()if()) и while(), но не после else
endif сделать во всех if?
можно вместо endif использовать ';' (проверкой на следующий символ, чтобы не понадобилось много штук? но можно ли тогда сделать match_if? тогда он не должен съедать ';', но может его оставлять в _textword):
ошибка if();{} исключается
ошибка if()if(){}else{} исключается
запрещено if()bla; else bla; надо if(){}else bla;
if()if(){};else{}; парсится так:
if()
if() {
}
;
else {
}
;
аналогично если while(){}; то исключается ошибка while();{}
но while в одну строчку не пишут, так что можно убрать ;
как сделать вложенные структуры? должны быть поля типа struct1->struct2.field1
???
работу со структурами надо прямо загнать в ix(одна),iy(вторая), нужны команды типа WITH для создания таких контекстов (или тип INDEXREGISTER)
проще всего структуру организовать через сложение ('.' будет операцией)
'.' в структурах даёт конфликт с системой модулей!!!
поля структуры могут быть и слева, и справа от = - УЧТЕНО
целые структуры присваивать не надо, но на них можно давать указатель
как хранить данные о структуре?
можно для каждого поля структуры хранить structname.fieldname с типом [и смещением (можно переложить на асм)]
а для самой структуры хранить размер в байтах для +SIZEOF(structname) - так же сделать +SIZEOF(arrayname) (для массива надо уметь вычислять размер из строки [])
нужен тип _T_STRUCT (ненакладываемый)
статическая структура:
если обращение к переменной с точкой, то надо уметь получить из записи structinstancename.fieldname строку structname.fieldname, чтобы получить тип [и смещение]
при этом надо сгнерировать structname+structname.fieldname
динамическая структура:
если обращение к переменной с ->, то надо уметь получить из записи structinstancename->fieldname строку structname.fieldname, чтобы получить тип [и смещение]
при этом надо сгенерировать pushnum(structname.fieldname), add(), peek() (в будущем (ix+...))
как выгружать строки сорца в асм? только через буферизацию строк в лексере? насколько большой проигрыш и можно ли сделать выигрыш?
1. STRMAX не придётся проверять на каждом символе? только если длина строки ограничена
2. можно tword собрать через strcopy (невыгодно для 1-2 символов)
3. не проверять 0x0d (его не будет в строке)
но как разрыв строки (из-за переполнения буфера) отличить от перевода строки??? если разрешена любая длина строки, то придётся проверять STRMAX при работе с tword!!!
loop1: //ждём нецифробукву (EOF не цифробуква)
_tword[_lentword] = _cnext;
INC _lentword;
_cnext = +(CHAR)readfin();
IF (_isalphanum[+(BYTE)_cnext]) goto loop1;
IF (_cnext == '\0') {
readfinstr(_tstr);
goto loop1;
};
goto loopgo;
loop2: //ждём недиерезис или EOF
_cnext = +(CHAR)readfin();
loopgo:
IF (+(BYTE)_cnext < +(BYTE)'!') { //ускорение выхода
INC _spcsize; //spaces after tword
IF (_cnext == '\n') {
INC _curline;
_spcsize = 0;
INC _waseols;
};
IF (!_waseof) goto loop2;
};
переделать выражение на match (с чтением следующего)
getnothing постараться убрать везде, кроме jp и пустого call (перенести в commands)
привести исходник к 80 символам (лучше 64)
перевести комментарии на англ (рус)?
условно компилировать в библиотеке те функции, которые использовались
ввести встроенные типы INT PFUNC, PBYTE PFUNC и т.п., PPROC, чтобы передавать обработчики (только без параметров)
но как сделать совместимо с Си?
CONST PBYTE PFUNC funcarray[3]={f1,f2,f3} не скомпилируется на Си
CONST PFUNC_PBYTE funcarray[3]={f1,f2,f3} скомпилируется
вызов: varfunc(), т.е. надо определить, какого типа метка - реальная функция (тогда call N) или указатель на функцию (тогда call jphl), другое - ошибка
T_PFUNC_PBYTE == T_PFUNC|T_POINTER|T_BYTE
T_FUNC_PBYTE == T_FUNC|T_POINTER|T_BYTE или просто T_POINTER|T_BYTE с 0 элементов?
можно буферизовать все переменные функции, но нельзя буферизовать все строки функции - там могут быть десятки килобайт строк!!! как тогда избежать вывода в три файла (код, переменные, строки) одновременно?
Пример:
char Arr[2][2] = {'J', 'a', 'n', 'B'},
* Ptr;
main ()
{
typedef char *ChrPtr;
Ptr = cast(Chrtr)(Arr+1)+1;
printf ("%c%c", **Arr, *Ptr);
}
Конверсия может быть явной и неявной. Явная конверсия является
выражением с предшествующим ключевым словом cast (только в Hisoft).
В Примере применен оператор конверсии к выражению (Arr + 1). При этом
выражение (Arr + 1), представляющее данное типа (char(*)[2]),
преобразуется в выражение, представляющее данное типа (char *), т.е. в
выражение такого же типа, как и Ptr.
Если данные типа (char) присваиваются переменной типа (int)
или (unsigned), то при этом самый старший байт этой переменной
заполнится нулями. И, наоборот, если данные типа (int) или (unsigned)
будут присвоены переменной тип (char), то переменной присвоится
младший байт данного. А если данное типа (int) присвоить переменной
типа (unsigned) и наоборот, то можно потерять данные.
Неявная конверсия может выполняться и при выполнении
арифметических операций:
- если некоторый аргумент имеет тип (char), то он будет преобразован к
типу (int);
- если только один аргумент 2х-аргументной операции имеет тип
(unsigned), то второй будет преобразован к этому же типу;
- в других случаях оба аргумента должны быть типа (int), таким же
будет и результат.
-1Функции с переменным числом аргументов-0
Функцию с переменным числом аргументов можно объявить перед
первым вызовом этой функции, а в заголовке определения,
непосредственно после скобки, замыкающей список параметров, должно
стоять слово auto. Если эти условия соблюдены, то каждый вызов функции
будет неявно дополнен аргументом типа (int), указывающим, сколько байт
заняли все аргументы функции, включая и новый аргумент. Следует
отметить, что аргументы вычисляются в порядке их выступления в вызове
и записываются в память в порядке убывания адресов. В частности,
результатом функции max является максимум переданных ей аргументов
типа (int):
int
max (Count) auto
int Count;
{
int argc, *argv, max;
argc = (Count >> 1)-1;
argv = Count + argc;
max = -32768;
while (argc--)
if (*argv-- > max)
max = argv[1];
return max;
}
-1UWAGA-0: Этого нет в базовом языке.
int
strcmp (Src, Trg)
char *Src, *Trg;
{
while (*Src == *Trg++)
if (!*Src++) return 0;
return *Src > Trg[-1] ? 1: -1;
}
far pointer'ы 4(3)-байтные
разадресация компилируется так:
если прошлая разадресация (peek) far pointer'а была с тем же именем far pointer'а, то страницу не включаем, иначе включаем
далее читаем как обычно по младшей части
(в недоасме нет случаев одновременного использования двух far pointer'ов)
в конце процедуры сбрасываем
в конце цикла сбрасывать - тормоз, но тогда надо гарантировать, что far pointer читали до цикла!
нельзя кастить из near в far и наоборот
при сложении-вычитании far pointer'ов получается far size_t (иначе пришлось бы генерить ошибку, если они в разных страницах)
по размерности pointer == size_t, т.к. оба показывают расстояние в байтах от какой-то точки
какой синтаксис? слово или просто ещё куча названий типов?
а как far call'ы? они нужны чаще?
пример из оригинального Bourne Shell'а):
#define BEGIN {
#define END }
#define SWITCH switch(
#define IN ){
#define ENDSW }
#define FOR for(
#define WHILE while(
#define DO ){
#define OD ;}
#define REP do{
#define PER }while(
#define DONE );
#define LOOP for(;;){
#define POOL }
подходы к построению парсера:
1. readword + compile_value
2. read_compile_value
3. match_value (при удаче делает readword)
как вызывать функцию с результатом по указателю? (только без параметров! т.к. мы передаём их как переменные) (надо знать тип функции! явно писать тип функции при таком вызове)
+CALL (funcname)? не сработает, т.к. сишник должен знать тип функции
разве что +CALLINT (funcname) и т.п., но так нельзя использовать производные типы
это всё равно что сделать +PEEKINT (poi) и т.п.
можно получить указатель на метку - но как делать goto по указателю? в стандартном Си этого нет, только в gcc
указатель на указатель можно сделать, если добавление указателя - это прибавление 15, а не 16
инлайн userg, nouserg? для этого надо used сделать массивом - как ускорить findrfree?
сделать версию, где функции компилятся по одной и есть цикл read-eval?
для этого надо кучу + динамическую структуру с текущими идентификаторами