Просмотр полной версии : ACK (Amsterdam Compiler Kit) для Вектора и 8080 вообще
ACK оказался не так уж и страшен. Собирается очень быстро и легко, компилирует почти нормальный Си (и еще кучу языков). uIP пришлось совсем чуть чуть подправить, но в общем можно сказать заработало из коробки.
Демо того же IoT-вебсервера, что был на ZPU, на сей раз в нативном исполнении:
https://www.youtube.com/watch?v=p3poXvOXEBU
Пухлость присуствует: нативный UIP.COM — 37003, тот же самый UIP.COM в варианте ZPU — 25488 это вместе с виртуальной машиной. Зато у ACK для CP/M практически полноценный комплект стандартных библиотек, printf() итд, работа с файлами.
Забавно, что целочисленный Мандельброт работает в 3 раза медленней, чем на ZPU, потому что там int32. sizeof(int) в нем 2 и долговязая арифметика тормозная.
Oleg N. Cher
29.12.2020, 21:32
ACK собирает под 8080, а не только под Z80? Интересно, не знал.
А на Модуле-2 там же тоже можно писать?
Oleg N. Cher, можно, но я пробовал только Си. Именно 8080 (среди прочих, список таргетов там внушительный). Ссылка на всякий случай https://github.com/davidgiven/ack
Oleg N. Cher
29.12.2020, 21:51
Тогда-с ждём игр под 8080-платформы на Модуле-2, раз на Обероне не хотите ;-)
Error404
29.12.2020, 22:24
ACK оказался не так уж и страшен. Собирается очень быстро и легко, компилирует почти нормальный Си (и еще кучу языков). uIP пришлось совсем чуть чуть подправить, но в общем можно сказать заработало из коробки.
Пухлость присуствует: нативный UIP.COM — 37003, тот же самый UIP.COM в варианте ZPU — 25488 это вместе с виртуальной машиной.
нативный Hitech-C: httpd.com 19кб
единственно, у меня был переписан printf для вывода только на экран (минус потоки STD), что сокращает бинарь на пару килобайт
ну и понятно, код Z80. Но любая операция с int32 - call в библиотеку (для чего была мысль опцией оптимизировать это в однобайтовые рестарты). Но работало с int32 достаточно быстро, по крайней мере FAT32 (который на int32 весь) не шибко тормозил
Oleg N. Cher
29.12.2020, 22:33
Hitech-C не собирает под 8080.
У меня эквивалент httpd.com еще с четырьмя килами дополнительных картинок и скриптов, но все равно понятно, что z80 дает возможность сгенерить код покомпактней. Тем не менее меня интересует именно 8080. Какой объем из этого — printf() итд я не знаю, потому что пока не нашел, как ack генерить map.
Доки вообще
http://tack.sourceforge.net/olddocs.html
Про генерацию кода для 8080
http://tack.sourceforge.net/olddocs/i80.html
Здорово! Я когда-то очень давно лет с 10 или более назад глядел на ACK, но потом что-то меня отвлекло и - не взялся.
Интересно поглядеть на то, как выглядит фрейм стека при вызове функции. Я тут недавно копался с Aztec C, пришёл в ужас. У них там процедура .csave для якобы уменьшения количества кода в стеке, это адъ. То, что она запихивает <BC> типа регистровую переменную, это норм. Но потом она образует фрейм для локальных данных за которым пихает предыдущий <SP> и адрес функции для размотки по выходу. Причём эта вся байда вызывается даже если нет локальных переменных: с нулями для оффсетов хотя достаточно было бы просто сделать pop b; ret; по выходу. Вот, где замедление...
Ну и да сканеры форматного ввода/вывода жуть сколько занимают. Я сразу отказался. Написал свой собственный консольный небуферизированный вывод без переадресации устройств: cputs, putch, getche плюс всякие putdec, putlx (для long) практически через прямые вызовы CP/M BIOS/BDOS по желанию. Только это сразу 4К сэкономило. А если всю conio на асме написать, наверное ещё кил можно срезать.
Oleg N. Cher
05.01.2021, 13:17
Интересно поглядеть на то, как выглядит фрейм стека при вызове функции.Не стОит ожидать здесь чуда. Всё-таки Си есть Си: косвенная адресация, прицел на рекурсию со всеми вытекающими.
Я вот придумал для нереэнтерабельных функций такой механизм передачи параметров:
#define Func(a, b, c) par1 = a; par2 = b; par3 = c; Func_Impl();
где par1-par3 глобальные переменные, доступ к которым осуществляется эффективнее (прямая адресация), чем к стековым (косвенная адресация; для Z80 чаще всего через (IX+n) или (IY+n) ). А реализация функции Func_Impl() достаёт параметры из этих глобальных переменных и сама может вообще не иметь фрейма (на практике она его всё равно имеет - для работы с локальными переменными. Правда, в SDCC можно выключить фрейм для функции модификатором __naked).
Это несколько громоздко записывается, но даёт неплохой код, что особенно важно для проца 8080.
- - - Добавлено - - -
P.S. В SDCC для асмовых функций есть возможность записывать такие переменные-параметры прямо в машкод функции. Пример описания такой переменной (Pt3Player_mode) можно посмотреть здесь:
https://github.com/Oleg-N-Cher/XDev/blob/master/ZXDev/Lib/C/Pt3Player.c
Не стОит ожидать здесь чуда. Всё-таки Си есть Си: косвенная адресация, прицел на рекурсию со всеми вытекающими.
[COLOR="silver"][SIZE=1]
Ну я совсем чуда и не ждал как-бы, видимо просто не совсем точно выразился. Я, скорее, хочу надеяться на наличие у ACK оптимизатора, который сможет эффекивно выбрасывать ненужное в определённых ситуациях. Один из частных примеров (отсутствие локальных данных, когда стек можно пихать навалом на фрейм вызова) я уже привёл. Это очень простая для оптимизатора ситуация, почему Manx её не сделал, мне до конца не ясно. Видимо, настолько в восторге были от факта что удалось портировать yacc парсер на 8080, что об остальном позабыли.
Понятно, что лучший способ нивелировать фрейм на вызываемой стороне - это link register либо что-то типа 8086 инструкций ENTER/LEAVE. Но за неимением такового нужен гибкий генератор входного/выходного кода. На это и уповаю.
Просто для 8080, в отличие от z80, совсем нет более-менее современных С компиляторов, всё K&R. Насколько я могу понять, ACK клэймит совместимость c X3.159. Я не смотрел, какая цифра после 159, надеюсь 26 или выше. Но в любом случае, это уже ANSI.
Тут на форуме winxru делал ANSI-совместимый кросс-компилятор, но кажется забросил. Замечательно, что svofski поднял тему ANSI-совместимых альтернатив. Язык C-это наше всё - самое то быстро писать CP/M-совместимые утилиты, даже если там вообще 0 оптимизаций. PL/I меня лично как-то меньше привлекает несмотря на то что да, код он сгенерит эффективней чем C.
Oleg N. Cher
05.01.2021, 22:01
PL/M компилер точно сгенерит код эффективней, чем Си. А насчёт PL/1 я не уверен. Не тестил.
Это очень простая для оптимизатора ситуация, почему Manx её не сделал, мне до конца не ясно.Это может быть связано с однопроходностью компилятора. В момент разбора синтаксиса по строкам и генерации кода для входа в процедуру у компилятора может ещё не быть информации, вызывается ли эта функция рекурсивно, или нет.
Ну, в общем, только тестировать, а не радоваться заранее.
Кстати, Ofront+ (Оберон в Си) умеет генерить на выходе K&R, если что. Ну и ANSI конечно тоже.
Язык C-это наше всё - самое то быстро писать CP/M-совместимые утилиты.Есть ещё Cowgol (http://cowlark.com/cowgol/index.html).
- - - Добавлено - - -
Мне больше всего интересно в этой теме:
a) применение ACK в качестве компилятора для разработки восьмибитных игр на Паскале и Модуле-2 для Вектора-06Ц, Специалиста, Апогея и др. платформ на КР580;
b) применение ACK в качестве бэк-энда при разработке игр на Обероне под эти же платформы.
На эти темы буду рад пообщаться.
PL/M компилер точно сгенерит код эффективней, чем Си. А насчёт PL/1 я не уверен. Не тестил.
Это может быть связано с однопроходностью компилятора. В момент разбора синтаксиса по строкам и генерации кода для входа в процедуру у компилятора может ещё не быть информации, вызывается ли эта функция рекурсивно, или нет.
Не совсем ясно, при чём тут многопроходность компилятора и возможность рекурcивного входа в функцию. Если нет локальных переменных, не нужен и фрейм под них. И stack adjust тоже не нужен, можно самого себя хоть до упомрачения вызывать, параметры просто лягут в стек вызова друг за другом ;).
А Manx такой оптимизации не делает.
Я, конечно, имел ввиду PL/M (не 1) Килдалла :)
хочу надеяться на наличие у ACK оптимизатора, который сможет эффекивно выбрасывать ненужное в определённых ситуациях
При очень поверхностном проглядывании исходников ack я видел только оптимизацию на уровне последовательностей команд 8080. Но кодогенерацию я смотрел только для 8080 и возможно есть и оптимизации на более высоком (более абстрактном) уровне
Oleg N. Cher
06.01.2021, 14:35
Не совсем ясно, при чём тут многопроходность компилятора и возможность рекурcивного входа в функцию.Смотрите: если компилятор однопроходный и простой - не строит семантическое дерево и его потом не обходит, а генерит код прямо в процессе синтаксического разбора - то в момент разбора функции и её параметров компилятор ещё не знает, рекурсивна ли эта функция. Кстати, рекурсия может быть ещё и косвенная ( Func() вызывает P(), а P() вызывает Func()) , такую отследить ещё сложнее.
Я, конечно, имел ввиду PL/M (не 1) Килдалла :)А я имел в виду Си. В PL/M нет рекурсии.
такую отследить ещё сложнее.
А я имел в виду Си. В PL/M нет рекурсии.
Вот вам код. Регистр <BC> содержит значение регистровой переменной. Я намеренно зарезервировал 3 байта под локальные данные чтоб было яснее.
Размер буфера под локальные переменные компилятор знает сразу же, на первом проходе, не даром C требует объявления всех локальных переменных в начале тела функции. При отсутствии локальных переменных, Manx тем не менее генерит
LXI D,0
call csave
ret
Разберитесь как это работает, и вам всё станет ясно, надеюсь.
foo() { char locals[3]; }
;Сгенерённый компилятором код
PUBLIC foo_
foo_: lxi d,.8
call csave
; char locals[3];
;}
RET
.8 EQU -3
Библиотечный код:
cret:
XCHG
POP H
SPHL
POP B
XCHG
MOV A,H
ORA L
RET
;
public csave,cret
csave:POP H
PUSH B
MOV B,H
MOV C,L
LXI H,0
DAD SP
XCHG
DAD SP
SPHL
PUSH D
lxi h,cret
push h
mov h,b
mov l,c
pchl
PS. Дико извиняюсь за hijack треда.
Oleg N. Cher
06.01.2021, 15:48
Разобрался: csave резервирует указанное количество байтов на стеке. А при чём тут рекурсия? :-D
Разобрался: csave резервирует указанное количество байтов на стеке. А при чём тут рекурсия? :-D
Я рад, что вы задали себе этот вопрос.
Oleg N. Cher
06.01.2021, 17:36
Да не себе, а лично Вам, PPC. Вы упомянули рекурсию, рассуждали о том, что в однопроходном компиляторе как-то можно определить является ли функция рекурсивной, а закончили постом хардкорного машкода, который вообще про локальные переменные. А в итоге пришли к тому, что я разговариваю сам с собой? Ну да ладно. Но мысли излагаете как-то запутанно.
aot: Так всё-таки найдётся джигит покодить на Паскале или Модуле-2 под i8080?
Да не себе, а лично Вам, PPC. Вы упомянули рекурсию, рассуждали о том, что в однопроходном компиляторе как-то можно определить является ли функция рекурсивной, а закончили постом хардкорного машкода, который вообще про локальные переменные. А в итоге пришли к тому, что я разговариваю сам с собой? Ну да ладно. Но мысли излагаете как-то запутанно.
aot: Так всё-таки найдётся джигит покодить на Паскале или Модуле-2 под i8080?
Укажите, где это я рассуждал о рекурсии и однопроходных компиляторах до того как вы такие слова мудрёные использовали?:v2_dizzy_facepalm:
Я и слово то такое "рекурсия" первый раз "упомянул" вам в ответ, тем более "однопроходность" :) :
Давайте учиться читать. Ваше творчество:
Это может быть связано с однопроходностью компилятора. В момент разбора синтаксиса по строкам и генерации кода для входа в процедуру у компилятора может ещё не быть информации, вызывается ли эта функция рекурсивно, или нет.
Мой ответ на это - где я первый раз "упомянул" (как вы это называете), рекурсию и "проходность":
Не совсем ясно, при чём тут многопроходность компилятора и возможность рекурcивного входа в функцию. Если нет локальных переменных, не нужен и фрейм под них. И stack adjust тоже не нужен, можно самого себя хоть до упомрачения вызывать, параметры просто лягут в стек вызова друг за другом ;).
Прочтите ещё раз мои посты 04.01 и 05.01:
Один из частных примеров (отсутствие локальных данных, когда стек можно пихать навалом на фрейм вызова) я уже привёл.
Какой хардкор, я привёл вам часть стандартной библиотеки компилятора и пример функции foo() { char locals[3]; } :)
Похоже, с вами рекурсивная диффамация приключилась. Бывает.
Oleg N. Cher
06.01.2021, 21:20
PPC, я не хотел бы замусоривать тему, а Вы, похоже, смакуете. Остаюсь при своём мнении: Вы приплели к рекурсии и невозможности её выявить на первом проходе какой-то кусок кода и какие-то локальные переменные. Это было не к месту. Dixi.
- - - Добавлено - - -
P.S. Насколько я понял: в сухом остатке у нас - ACK по качеству кода ну может чуть получше, чем BDS-C. Это печально.
PPC, я не хотел бы замусоривать тему, а Вы, похоже, смакуете. Остаюсь при своём мнении: Вы приплели к рекурсии и невозможности её выявить на первом проходе какой-то кусок кода и какие-то локальные переменные. Это было не к месту. Dixi.
- - - Добавлено - - -
Ещё раз вам повторю: приведите пример моими словами о рекурсии, о необходимости или невозможности её выявить о проходах и прочем.
Своими постами вы загораживаете мне ivagor-а, и его дельный ответ по существу.
Сравнивать BDS C и ACK неуместно. BDS C это даже не K&R. Какая разница, какое у BDS C качество кода, если им нельзя скомпилировать ничего кроме othello.c? ACK собирает исправно работающий uIP, это неплохое доказательство состоятельности его как компилятора.
Что до того, какой код генерит ACK. Почему бы просто не почитать то, что он генерит? startrek.c подойдет?
Без оптимизаций:
https://pastebin.com/Ti4chcvw
-O6:
https://pastebin.com/BTxMFibL
Сколько префиксов перед пабликами у стандартных функций, аж 3
У UDF - одно подчёркивание
Очень недурно на первый взгляд.
Мне понравилось как делается фрейм
call .probyte
.data -LOCAL
Рестарты на полную катушку, это здорово как засылка параметров в функции, все эти .fstoreX
Чуть напрягает
jmp .ret
по выходу
и знакомое
mov a,m
mov l,a
mvi h,0
Понравилась оптимизация в maneuver_energy e = e - n - 10
Компайлер сделал e = e - (n+10)
Aztec так не умеет
Но в общем - классно на первый взгляд, а в деталях надо смотреть более пристально.
Error404
07.01.2021, 12:52
P.S. Насколько я понял: в сухом остатке у нас - ACK по качеству кода ну может чуть получше, чем BDS-C. Это печально.
тут беда не в компиляторах, а в i8080, которому до нормального процессора не хватает ни РОН ни способов адресации. Вот и получаются семь шапок из овчинки. Поэтому на Z80 обычно С ложится лучше (хотя тоже далеко не идеал из-за желания Z80 быть архитектурно близким к 8080). Даже не смотря на то, что индексные или альтернативные регистры в Z80 это плюс байт/префикс к команде и это казалось бы даст большую трату кода - но нет. Поэтому - да, смириться и искать компилер С для 8080, который хотя бы понимает ANSI, и синтаксис исходников сложных приложений пережовывает
Oleg N. Cher
07.01.2021, 12:53
приведите пример моими словами о рекурсии, о необходимости или невозможности её выявить о проходах и прочем.PPC, если Вы вникнете в суть форумного (да и не только) общения, то поймёте, что общение (исключая родственных душ) это Вавилон: каждый говорит только о своём. Я пытался развить тему, что в ACK же ещё есть Модула-2 и Паскаль! Но это оказалось никому не интересно. По существу.
Так вот. Свою пользу от присутствия здесь я нахожу в том, что я рассказал вам, энтузиастам, работающим с кодом для процессора 8080, об идее передачи параметров внутрь функции без фрейма. Через глобальные переменные. Ведь компилятор может сам так делать. Потому что само наличие фрейма, ввиду отсутствия в 8080 аналогов команд ENTER/LEAVE или даже индексного регистра для адресации параметров на стеке, делает обращение к параметрам (и к локальным переменным) громоздким и неэффективным. Мой способ конечно специфический, обладает рядом ограничений, но всё-таки до него ещё не додумались авторы компиляторов Си для 8080.
Функция не должна иметь локальных переменных. Гипотетический однопроходный компилятор способен это вовремя обнаружить.
Функция не должна быть реентерабельной, т.е. иметь прямую или косвенную рекурсию. Гипотетический однопроходный компилятор будет иметь трудности с определением этой ситуации в момент принятия решения: генерировать фрейм или нет?
Это то, что было у меня на уме, когда я писал посты в эту ветку. А Вы же, PPC, принялись рассуждать о локальных переменных и о том как их лучше размещать на стеке, да и привели ещё и код. А это частности. Намного более любопытна задача: как определить на момент принятия решения "генерить ли фрейм" - рекурсивна ли эта функция? Я написал, что в однопроходном компиляторе это сделать почти невозможно. А Вы принялись опять "при чём тут многопроходность, я вот так размещаю лок. переменные на стеке". Это вообще разговор слепого с глухим. Но я не удивлён, потому что Вы о своём. Постарайтесь ухватить мой посыл хотя бы немного.
Своими постами вы загораживаете мне ivagor-а, и его дельный ответ по существу.Не умаляя достоинств ivagor'а: дельный ответ по существу чего? Просто совпало? :-D
Извините за оффтоп, но в теме уже упоминались другие компиляторы C кроме ack, вот еще один (https://github.com/z88dk/z88dk/wiki/Classic-8080)
Kakos_nonos
19.01.2021, 21:19
О, вот это круто!
- - - Добавлено - - -
А под Windows может кто-нибудь собрать компилятор ACK? Буду премного благодарен!
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot