|
nedoPC.orgCommunity for electronics hobbyists, established in 2002 |
|
Программирование на компиляторах ЯВУ для ретро ЭВМ
Author |
Message |
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Появился небольшой опыт в написании программ для КР580 для систем без DOS CP/M на Паскале МТ+. Этот Паскаль написан в 1978...1980 году, причём не одним человеком (как было с Си Aztec и BDS, которые написали частники), а фирмой, т.е сразу многими программистами. Потому уровень этого Паскаля высокий. К сожалению, разработчики стремились совместиться со стандартом Паскаля того времени, потому на 95% реализован классический Паскаль, описанный в документах Н.Вирта.
Для сравнения Турбо-Паскаль появившийся 5 лет спустя, ввёл много мелких полезных доработок, нестандартных, но упрощающих программирование (и впоследствии ставших стандартом). Например, в МТ+ нельзя ввести в символьную строку управляющие символы, что без труда делается в TP (например, #13#10'Привет' или ^]^Y#32#32), метки для переходов - только цифровые (как в бейсике), а не символьные, как в TP и других развитых ЯВУ, узнать адрес можно только для переменной или процедуры расположенной в том же самом файле (для других модулей это уже не работает). Встроенные инлайн-ассемблер (понимающий HEX-дампы и мнемонику КР580) не допускает меток, что существенно снижает его ценность. Объём и, соответственно, скорость генерируемого кода, как общепризнано, хуже, чем у TP. Есть ещё много других мелких неприятностей. Компиляция длится на порядок дольше, чем в TP. Вообще программирование на этом Паскале очень сильно отличается от программирования в борландовских продуктах на PC и GNU Pascal Compiler в Linux. Но у МТ+ есть неоспоримое преимущество перед Турбо-Паскалем, это линковка модулей и процессор КР580. TP очень хорош, но из-за нелинкующего принципа для медленных ЭВМ он имеет низкую полезность и по сути может считаться игрушкой.
Мне и ранее было известно, что МТ+ генерит менее эффективный код, чем BDS Си, AZtec Си и Турбо-Паскаль. Например, Турбо-Паскаль даёт объём кода примерно в 4 раза больший чем полный аналог написанный на ассемблере. Но тут я специально сравнил МТ+ с асссемблером и объём кода на МТ+ на программах в 300-400 строк Паскаля получился в 7-10 раз больший, чем у аналогов написанных на ассемблере.
Вопреки ожиданиям, при трансляции в эмуляторе эффективность разработки на этом компиляторе не стала на порядок быстрее, чем на ассемблере. Тормозит медленная трансляция в эмуляторе 8-ми разрядки.
Если при программировании на ассемблере при каждой перетрансляции технические потери составляют всего секунду (хлоп по кнопке и можно проверять прогон в эмуляторе), то трансляция в эмуляторе CP/M-компилятором 1000 строк длится 6-8 минут, хотя потерь времени на запуск эмуляторов ОРИОНА или РК86 для проверки нет, т.к и транслируется в эмуляторе ОРИОНА. С реальным дисководом время трансляции - вдвое выше. А для проверки программ для РК86 запускается эмулятор РК86 в эмуляторе ОРИОНА (т.е происходит двойная эмуляция: PC эмулирует ОРИОН, ОРИОН эмулирует РК). А ещё одной причиной тормознутости является то, что за одну итерацию находится и исправляется только одна ошибка, т.к встретив ошибку компилятор останавливается. А ассемблер сразу выдаёт кучу ошибок и их все можно исправить зараз, за одну итерацию.
Улучшить свойства программы на Паскале можно заменив в РК86 процессор на Z80. Это устраняет проблему отсутствия меток в инлайн-ассемблере, т.к у Z80 есть JR-команды. И хотя встроенный в МТ+ инлайн-ассемблер не понимает мнемоники Z80, но можно совместимые команды писать в мнемониках КР580, вставляя вместо JR-команд просто байты. Чтобы не трахаться с набором ассемблерных текстов, удобнее транслировать ассемблерные вставки на Z80 в обычном ассемблере, генерировать дамп и в ассемблерные функции и процедуры в Паскаль-программе вставлять странслированные дампы, а не набирать текст в мнемониках.
К сожалению, похоже Microsoft не написала компилятор Паскаля для КР580/Z80, а только компилятор фортрана. Можно попробовать компилятор фортрана. На нём удобно написать программу рассчёта траектории полёта на Марс, но для написания игр для РК86 он вряд-ли будет удобен, т.к в нём нет указателей, ассемблера и невозможно работать по железу.
Интересно есть-ли кросс-компилятор Паскаля работающий в MSDOS или Windows на PC, выдающий на выходе код для процессора КР580/Z80 ?
Для компьютера Полдин-601 болгары написали компилятор Паскаля в двух версиях - одна в кодах CPU 6800 работает на реале, а вторая в кодах 8086 работает на PC в MSDOS. Если такого компилятора нет, то остаётся только заменять КР580 на 6802 в РК86 или же использовать конвертор Паскаль-программ в программу на Си, а далее транслировать в коды КР580 с помощью кросс-компилятора Си.
Увы, это тоже не варианты, т.к 6802 для нас непривычный и заметно уступает КР580 по производительности (при том же цикле доступа к ОЗУ). А использование конвертора Паскаль в Си громоздко, чревато доп.ошибками, а главное, всё-равно бесполезно. Встретил лишь единственный кросс-копилятор Си в коды КР580, это компилятор от vinxru, но он не стандартный, а документации нет. Выглядит так, как будто на Западе вообще никто не писал кросс-компиляторов для КР580 и похоже, что вне пределов бывшего СССР процессор КР580 непопулярен. - - - Добавлено - - -
Но в CP/M кроме Си, Паскаля и фортрана есть ещё несколько старых и несколько более новых ЯВУ, являющихся производными от Паскаля. Кстати, от Си ничего производного нет, т.к С++ возник позже, да и похоже мощности 8-ми разрядки для ООП не хватило.
В качестве старых ЯВУ надо упомянуть ЯВУ PL/M и PL/1. Первый очень близок к ассемблеру и считается, что как ЯВУ он не очень мощный (хотя до появления компиляторов Паскаля в конце 70-тых, был популярен). Более древний PL/1 немного похож и на PL/M и на Паскаль и скорее всего вполне подойдёт для разработки ПО для РК86. Его компилятор для КР580 выглядит солиднее, чем компилятор PL/M. Есть ещё несколько CP/M-компиляторов АДА для КР580. Это язык производный от Паскаля и, очень похоже, что его тоже вполне можно использовать, т.к он имеет встроенный ассемблер. Есть и кросс-компиляторы языков АДА и ОБЕРОН, но они тоже, увы, лишь для Z80.
Last edited by barsik on 06 Nov 2018 22:03, edited 2 times in total.
|
27 Oct 2018 07:15 |
|
|
Lavr
Supreme God
Joined: 21 Oct 2009 08:08 Posts: 7777 Location: Россия
|
Есть такой, и похоже, что даже не один, если хорошо поискать... Скачал чисто для проверки, не битая ли ссылка: Но поскольку мне он не нужен, то удалю...
_________________ iLavr
|
31 Oct 2018 05:05 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Если бы программисты для Микроши читали журнал МПСС, то им бы не потребовалось адаптировать DDT CP/M. Это сделали задолго до этого разработчики ИРИШИ и очистив DDT от ненужных дисковых функций, доработали его и опубликовали в журнале МПСС в 1986 году. Насчёт кодов Z80 в DDT. Видимо речь о ZDDT, это универсальная версия DDT, что работает на КР580. Раз этот DDT был с адреса 100, значит это вообще неадаптированный DDT. Мне среди ПО РК попадались лишь уже адаптированные версии DDT (уже перемещённые под вершину ОЗУ). В оригинальном DDT в его начале стоит процедура переноса кода под вершину TPA по таблице коррекции. Для получения кода DDT работающего в вершине ОЗУ ставят стоп точку (или JMP F86C) в конце процедуры переноса и подставив в адрес 6 значение из ячейки RAMTOP, чтобы задать TPA, делают запуск с адреса 100. После чего код DDT настроенный на адреса берётся из вершины ОЗУ, а для завершения адаптации остаётся в полученном коде, уже настроенном на верхние адреса, найти все CALL 5 для консольных вызовов, и заменить их на CALL F803/F809. Но можно этого не делать, а использовать код CP/M-программы в оригинале, добавив к ней частичный эмулятор BDOS CP/M. Причём для недисковой программы эмулировать требуется только консольные функции - 1, 2, 6, 9 и 10. Первыми подобный эмулятор консольных функций CP/M (на адрес 0...FF) написали и опубликовали авторы РК86 ещё в 80-тые годы. Он предназначался как раз для использования на РК недисковых CP/M-программ и чтобы программы РК написанные с использованием CALL 5, в будущем, когда на РК появится CP/M (на базе RAM-диска или даже с настоящим дисководом), программы не пришлось бы адаптировать для неё. К сожалению, этот призыв остался без внимания, не было написано ни одной игры совместимой с CP/M. Наоборот авторы-любители постарались нарушить все правила хорошего стиля и нагло лезли не только к клавиатуре и экранному буферу, но и внутрь ПЗУ. Эмулятор функций BDOS перехватывает вызовы из программ командой CALL 5. Для этого на адрес 5 ставится или JMP в эмулятор BDOS размещённый в вершине ОЗУ или размещённый здесь же (если он умещается в адресах 5...FF). Такой программе достаточно эмулировать лишь недисковые функции BDOS CP/M, что позволяет использовать программы CP/M до тех пор пока они не лезут к диску. Программы полученные из под компиляторов ЯВУ, например Паскаля, являются полноценными CP/M-программами, в которых стандартные фунции READ и WRITE для ввода/вывода на консоль выполняют вызов BDOS по адресу 5. Потому для написания на ЯВУ программ для РК86 работающих без CP/M операторы READ/WRITE непригодны для ввода/вывода и можно использовать только специально созданные процедуры написанные на встроенном ассемблере. Которые очень просты, т.к они делают только вызов стандартных подпрограмм ПЗУ F803 и F809. Хотя использование собственных процедур ввода/вывода быстрее и удобнее, но начинающему изучение Паскаля всё-же желательно, чтобы все операторы работали как в учебнике, чтобы ввод/вывод, по крайней мере на консоль, делался стандартными операторами READ и WRITE. Чтобы без модификации кода компилятора или переопределения некоторых процедур низкого уровня RUN-тайм библиотеки, работали стандартные операторы READ и WRITE как раз годится эмулятор консольных BDOS-функций. Это простейшая программка размером всего в 256 байт и я ещё в начале 90-тых написал такой эмулятор для адаптации отладчиков. Только я грузил его не в Zero-Page, а под вершину ОЗУ, чтобы можно было отлаживать программы с адреса 0. И этот же код годится для того, чтобы на без-CP/M-ном РК86 или ОРИОНЕ можно было использовать программы написанные на ЯВУ. Для использования на РК программ созданных для CP/M, в том числе и написанных на Паскале, удобно в свободный участок ПЗУ, например, в адреса F000...F7FF прошить эмулятор консольных функций CP/M (естественно, перетранслировав на соответствующие адреса). Нижеприведённый простой эмулятор консольных функций BDOS имеет размер всего в 256 байт и позволяет без модификаций использовать все недисковые (и нелезущие в CP/M-BIOS) программы CP/M. Перед стартом CP/M-программ, написанных самостоятельно на ЯВУ или взятых из архивных сайтов CP/M достаточно выполнить инициализацию (при которой формируются JMP-ы в адресах 0000, 0005 и JMP на вход в BDOS в вершине ОЗУ). Кстати, не все CP/M-программы работают только через BDOS. Есть CP/M-программы, что для ускорения или по иным причинам делают вызовы консольных функций BIOS. Полноценный эмулятор CP/M должен это учитывать. Впрочем, подобный эмулятор полезен только для прогона новых программ написанных на ЯВУ. К сожалению почти все игры CP/M (кроме убогих диалогово-текстовых) рассчитаны на экран шириной в 80 символов, а остальные программы дисковые. эмулятор консольных функций BDOS CP/M | | | | Code: ; Эмулятор консольных функций BDOS CP/M. Может располагаться в Zero- ; Page CP/M (т.к имеет размер всего в 255 байтов). Этот код в 256 байт ; просто подставляется в начало бездисководной CP/M-программы, после ; чего программа должна стартовать с адреса 0 и сможет работать без ; CP/M. Если Zero-Page нужна для RST, то можно грузить этот BDOS под ; вершину ОЗУ (на 7500), а удобнее всего прошить в ПЗУ в области F000. .Z80 aseg ORG 100H MYBDOS EQU 75F6H
public MYBDOS
MCONIN EQU 0F803H MCOUT EQU 0F809H MSTAT EQU 0F812H XF81B EQU 0F81BH WBOOT EQU 0F86CH
STACK EQU 076D0H
; ----------------------------------------------
LOOP MACRO ADDR DEC BC LD A,B OR C JP NZ,ADDR ENDM
; ----------------------------------------------
.phase 0 JP OOO DS 2 JP MYBDOS
OOO: LD HL,WBOOT LD (1),HL
LD A,0C3H LD (MYBDOS),A LD HL,XBDOS LD (MYBDOS+1),HL
.comment \ LD HL,DATA LD DE,MYBDOS LD BC,@LENGTH @LDIR: LD A,(HL) LD (DE),A INC HL INC DE LOOP @LDIR \ JP 100H DATA:
; ----------------------------------------------
; .dephase ; .phase MYBDOS
XBDOS: LD HL,0 ADD HL,SP LD (TMPSTK),HL
LD SP,STACK LD A,C OR A JP Z,0 ; WBOOT ; функция 0 (WARM BOOT)
CP 12 ; ф.12 номер версии JP NZ,JJJ_01 LD A,22H RETURN: defb 21H ; LD HL,(TMPSTK) TMPSTK: DS 2 LD SP,HL LD L,A LD H,0 RET
JJJ_01: LD HL,RETURN PUSH HL
LD C,E CP 1 JP Z,FCONIN ; F.1 CONIN CP 2 JP Z,MCOUT ; F.2 CONOUT CP 6 JP Z,FUNC_6 ; F.6 direct I/O CP 9 JP Z,MSGCPM ; F.9 MSSG CP 10 JP Z,FRDBUF ; F.10 RD_BUFF CP 11 JP NZ,WBOOT NSTAT: PUSH HL ; F.11 STATUS LD HL,COUNT LD A,(HL) OR A JP Z,NSTA_2 DEC A LD (HL),A XOR A ; JP NSTA_3 POP HL RET
NSTA_2: INC HL LD A,(HL) OR A JP NZ,NSTA_1 CALL XF81B INC A JP Z,NSTA_3 DEC A LD (KBDBUF),A NSTA_1: LD A,255 NSTA_3: POP HL RET
; -------------------------------------------------
MSGCPM: ; PUSH HL ; надо, если MCOUT в компьютере портит регистры ; PUSH BC EX DE,HL MSGLOO: LD C,(HL) LD A,'$' CP C ; JP Z,POP_BH RET Z CALL MCOUT INC HL JP MSGLOO
POP_BH: ; POP BC ; POP HL ; RET
; -------------------------------------------------
COUNT: defb 255 KBDBUF: defb 0 ; должны идти подряд
; -------------------------------------------------
FUNC_6: INC E JP NZ,MCOUT CALL MSTAT RET Z JP MCONIN
; -------------------------------------------------
FCONIN: ; PUSH HL ; надо, если MCONIN в компьютере портит регистры ; PUSH BC LD HL,COUNT LD (HL),255 INC HL ; KBDBUF LD A,(HL) OR A JP NZ,CONI_2 CALL MCONIN CONI_2: CP 3 JP Z,0 ; WBOOT LD C,A PUSH AF CALL MCOUT POP AF ; POP BC ; POP HL RET
; -------------------------------------------------
FRDBUF: EX DE,HL LD C,(HL) ; BUFF SIZE LD B,0 INC HL RDBLOO: CALL MCONIN CP 8 JP Z,ZABOJ PUSH BC LD C,A CALL MCOUT POP BC CP 13 JP Z,RDDONE CP 10 JP Z,RDDONE CP 3 JP Z,0 ; WBOOT ; CNTRL-C INC HL LD (HL),A INC B LD A,C CP B ; конец буфера ? JP NZ,RDBLOO LD B,C RDDONE: INC DE EX DE,HL LD (HL),B RET
; -------------------------------------------------
ZABOJ: LD A,B OR A JP Z,RDBLOO DEC B DEC HL PUSH BC LD C,8 CALL MCOUT LD C,20H CALL MCOUT LD C,8 CALL MCOUT POP BC JP RDBLOO
; -------------------------------------------------
@ENDBDOS EQU $
BDSIZE EQU $-MYBDOS @LENGTH EQU $-MYBDOS
@FREE EQU 7600H-$ .dephase
if low $ and 7FH rept 80H-(low $ and 7FH) defb 255 ENDM endif
END
| | | | |
|
06 Nov 2018 02:42 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Тему, к сожалению, увели в сторону и засорили не относящимся к теме флеймом. Но всё-же надо возвращаться к исходной теме, - выбору оптимального компилятора ЯВУ для программирования ретро ЭВМ. Сомневаюсь. Для КР580 нет, т.к похоже, что КР580 вне России мало популярен. И даже для Z80 изображённое на картинке IDE скорее всего полуфабрикатное демо (типа студенческой работы). Возможно для Z80 пригодные кросс-Паскаль-IDE всё-же есть, но платные (есть кросс-компиляторы Паскаля для AVR и ПЛИС, а изменить целевую платформу разработчику нетрудно). Иначе на всех подобных форумах не было бы таких же вопросов и попыток сделать их самому. Конечно интегрированная среда ускоряет разработку. Это чётко показал ещё си-пи-эм-овский Турбо-Паскаль 3.0 (но он, увы, для Z80). А пока, если используется Win XP (что позволяет запуск MSDOS программ), то используя MSDOS TSR-эмулятор CP/M и Паскаль МТ+, потери времени на компиляцию снижаются почти до нуля, а имеющийся в нём отладчик, хотя и не так удобен как встроенный в IDE, но тоже упрощает отладку. Так что эффективность разработки, относительно разработки в среде кросс-IDE, страдает не существенно. Наконец-то и эта неприятная проблема успешно разрешилась. Совсем идеально с ассемблером было бы, если бы встроенный инлайн-ассемблер использовал мнемоники Z80 вместо устаревших мнемоник КР580... но всё идеально бывает только в мечтах. Освоение Паскаля начинающим, особенно после бейсика, по сравнению с освоением Си, происходит намного легче и быстрее. Сужу по себе, в начале 90-тых пытался освоить Си, но не преуспел за много месяцев. Тогда как бейсик-компилятор был освоен мгновенно. После чего и Турбо-Паскаль был освоен всего за две недели. Позднее, я больше имел дело с Си, но как раз потому, что Паскаль (хотя и не МТ, а Турбо) был моим первым ЯВУ, отношусь к нему с симпатией. Но для программиста, особенно при работе по железу и ячейкам, Си всё-же, кажется, удобнее. По-крайней мере, старый классический Паскаль (сделанный по Н.Вирту) в некоторых аспектах кажется менее удобным, чем старый классический Си (сделанный по Кернигану и Ритчи). Потому, если у ретро-любителя мозг ещё не отравлен бейсиком (некоторые учёные утверждали, что после бейсика уже невозможно научить человека правильно программировать), то в качестве ЯВУ для ретро-программирования лучше выбрать Си, а не Паскаль. Классический Си ближе к железу, в нём удобнее работа с указателями, тогда как в классическом Паскале из 70-тых указатели используются в основном для работы с динамическим объектами (позже в TP работу указателей улучшили). В Си нет строгой несовместимости похожих типов, как в Паскале, что защищает от ошибок, но снижает гибкость. Правда указатели в режиме ослабленного контроля, а тем более встроенный ассемблер позволяют обойти все строгости Паскаля, но это "химия". Особенно утомляет несовместимость числовых типов с boolean (и true это не любое, отличное от 0, число, а именно 1). Потому нельзя, как в BDS C, в качестве условия в условных операторах написать числовое выражение или даже просто переменную. Работа с файлами существенно отличается от последующих стандартов (заданных Турбо-Паскалем), примеров в описании нет, пришлось выяснять их применение экспериментально. И вопреки нижеследующей переводной цитате, где написано, что МТ+ лучше, чем BDS С, в отличие от МТ+, с BDS у меня лично вообще не было проблем, даже ассемблер для трюков был не нужен (хотя возможно мне просто не встретились упомянутые ниже глюки BDS C). | | | | Quote: Хотя к настоящему времени в значительной степени забытый, Паскаль-MT+ был в свое время очень влиятельным продуктом ... К 1983 году о Паскале-MT+ было доступно больше книг, чем о всех других компиляторах Паскаля вместе взятых ... Если вы хотите программировать в CP/ M на C в конце 70-х, то у вас был выбор: либо тратить свою жизнь на бесконечные перестановки дисков, открытие и закрытие редакторов, компиляторы, компоновщики и другие утилиты, либо потратить свою жизнь на написание программ в борьбе с несовместимостью и бесконечными ошибками BDS C. Это было одной из тех вещей, которые привели людей из C и к компиляторам, таким как Pascal на небольших микрокомпьютерных системах ...
Первоначально подмножество Паскаля по имени Паскаль-MT+ использовало тот факт, что было гораздо проще написать компилятор для высокоструктурированного языка Pascal, чем для неструктурированных C или классических языков, таких как Fortran, в дополнение к тому, что Pascal намного меньший язык. Написанная на языке ассемблера, она выполняла тот же трюк, что и BDS C, - выполнение компиляции фактически внутри доступной памяти, и в отличие от продукта BDS, система Паскаль-MT+ не содержала ошибок, документация была ясной и лаконичной, и компания активно развивала обновления и исправления ошибок. Компилятор также имел встроенный компоновщик, который автоматически связывал объектный код при компиляции, новшество в то время, имел символический отладчик, редактор и предлагал поддержку наложения оверлеев, в результате чего скомпилированная исполняемая программа загружала в память только ту часть кода, которая была необходима, когда конкретный сегмент программы выполняется.
| | | | |
Паскаль господствовал в программировании почти до конца 80-тых, но как пишут, не столько из-за превосходства Си, сколько из-за того, что Unix и Си поставлялись в университеты бесплатно, проиграл в конкуренции с Си. Как только в промышленность пришли бывшие студенты обучавшиеся на Unix и Си, Паскаль был обречён и даже незаслуженно стал презираемым, почти как бейсик.
|
09 Nov 2018 00:09 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Хотя в рассмотрение ещё не попал PLI от Digital Research (а Гарри Килдэлл считал его самым эффективным инструментом для процессора 8080), можно подвести кое-какие итоги. Если доступен только процессор КР580, то считая самым важным критерием хотя-бы возможность написать реальную программу на ЯВУ, побеждает PL/M. Пусть и самый неудобный в разработке, PL/M это ЯВУ дающий реальный шанс написать программу для 8-ми разрядки на ЯВУ. Про другие ЯВУ такого не скажешь (если не считать программами крошечные поделки). PL/M побеждает по плотности кода практически без конкуренции. Он даёт плотность кода лишь в 1.5 раза худшую, чем ассемблер. В этой гонке ближайшие конкуренты Паскаль и Си остались далеко за горизонтом. Сейчас PL/M совсем не популярен. Применению PL/M у отечественных любителей помешало его отсутствие, как класса, т.к компилятор имелся лишь для ОС ISIS (СМ-1800), а на отечественных самоделках этой ОС не было. Впрочем и других ЯВУ на отечественных бытовых ЭВМ в периоды их массового использования (из-за отсутствия дисководов) тоже практически не было, - дисководы, DOS и компиляторы ЯВУ стали доступны слишком поздно, в основном в 1993 году, после заката популярности бытовых и любительских ЭВМ. Но сейчас ситуация изменилась. Недавно был открыт компилятор PLMX и каким-то людям удалось конвертировать на Си, странслировать и научиться использовать на IBM PC, исходно работающий на майн-фрейме, оригинальный компилятор PL/M. Ещё раз подчеркну, что хоть разработка на PL/M и не так удобна, как на других ЯВУ (некоторые из которых имеют даже IDE), но она хотя-бы даёт реальный шанс написать на ЯВУ программу, которую ранее можно было сделать только на ассемблере. При КР580 конкуренты PL/M по плотности кода значительно отстают (ближайший конкурент Паскаль МТ+ хуже, чем PL/M по плотности выходного кода аж в ~3 раза). Но для Z80 ситуация лучше. После PL/M самым эффективным по объёму кода, а уж тем более по скорости разработки, является Турбо-Паскаль. Судя по моему небольшому опыту с TP, он даёт разбухание объёма кода (относительно голого ассемблера) примерно в 3 раза (естественно это не точная величина, для каждой программы это соотношение своё). Хотя это вдвое хуже, чем у PL/M, но это с избытком компенсируется на порядки большей скоростью разработки программ (примитивное IDE в TP даёт даже возможность отладки). На третьем месте в кандидаты на оптимальное ЯВУ для ретро любителя будет какой-либо Си, с коэффициентом разбухания кода и торможения ~4. Кстати, современные версии кросс-Си для Z80 всё ещё сопровождаются разработчиками и даже развиваются. Возможно они уже могут конкурировать по плотности кода с Турбо-Паскалем (хотя по удобству, скорости разработки точно нет). Для машин на КР580 Турбо-Паскаль, т.к он для Z80, выпадал из рассмотрения. Хотя PL/M даёт вдвое лучшую плотность кода, чем Турбо-Паскаль, но сейчас гораздо важнее скорость разработки. А в этом Турбо-Паскаль на порядок, если не на два эффективнее, чем PL/M. Потому при Z80, если важны удобство программиста и скорость разработки (т.е если нужен результат, а не просто поупражнять мозги), то вывод очевиден, - альтернативы Турбо-Паскалю нет. И лишь, если объём кода разбух выше допустимого, а замена части процедур на ассемблер не помогает, то придётся использовать PL/M. К сожалению, включение процедур на ассемблере в TP менее удобно, чем в компиляторах с линковкой. Это плата за скорость компиляции. По совокупности параметров, возможно, что все эти 3 варианта примерно равноценны (другие преимущества Си или Паскаля уравнивают потерю в объёме кода относительно PL/M). Я занялся PL/M лишь оттого, что ранее мне был нужен код для КР580, но сейчас из-за изменившихся обстоятельств ограничение в выходном коде именно под КР580 исчезло. Кстати, при использовании PL/M с Z80 полезна программа оптимизатор, которая сканирует исходник в кодах Z80 и отыскивает команды JP, которые можно заменить на JR (в идеале даже переставляя фрагменты текста, чтобы увеличить процент команд JR). Почему никто не встроил такую функцию оптимизации в ассемблер, это же совсем несложно? А умный конвертор мог бы оптимизировать под JR даже программу в кодах КР580. Если же целевой процессор 6502, то для него нет ни PL/M, ни Турбо-Паскаля (да и других Паскалей нет). Для него пригоден лишь третий по эффективности вариант - использование кросс-компилятора Си CC65.
|
01 Nov 2019 11:51 |
|
|
Lavr
Supreme God
Joined: 21 Oct 2009 08:08 Posts: 7777 Location: Россия
|
А откуда все эти данные про " по плотности выходного кода аж в ~1.5 раза)... ~3 раза)... ~4 раза"?
_________________ iLavr
|
01 Nov 2019 17:03 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Из моих предыдущих постов ответ на этот вопрос любому совершенно ясен. Специально для вас ещё раз уточняю, что с Паскалями и Си для CP/M я имел дело сам и могу судить. К тому же я чётко указал, что это примерные лично мои данные, и скорее всего даже завышенные (в пользу компиляторов Си и Паскаля). Оригинальный PL/M я не тестировал (лишь PLMX, который я думаю не хуже), но в википедии написано, что PL/M ещё эффективнее, чем я пишу, а разработчик PL/M приводил в своих статьях ещё более лучшие данные по плотности кода. Более точные данные можно получить лишь специально сравнивая одну и ту же программу, лишь реализованную разными языками. Да и тогда данные не будут точными, потому что для разных программ соотношение объёмов кода будет разное. Эффективности современных кросс-компиляторов Си под Z80 я не знаю, потому и не приводил. Интересно было бы сравнить. У меня есть исходник моей программы в 3000 строк на BDS C и ассемблере, но сравнить с другими Си без долгого траха не получится, т.к это не чистый Си (много ассемблера, а механизмы его включения у разных компиляторов разные), да и есть отличия в синтаксисе, объёме реализованных операторов и их работе.
|
01 Nov 2019 21:18 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
При 8-ми разрядке на процессоре Z80 при выборе компилятора ЯВУ почти нет альтернативы Турбо-Паскалю. Именно поэтому Паскаль чуть не победил Си в 80-тые годы, он был популярнее, чем Си. Лишь когда Си породил C++, а Паскали с поддержкой ООП опоздали, то в конце 80-тых Си стал популярен, а к середине 90-тых уже сильно потеснил Паскаль, хотя Дельфи позволил Паскалю ещё долго оставаться полноценным ЯВУ для Windows. Турбо-Паскаль не только даёт плотный код. Главное, - он в десятки раз ускоряет процесс разработки программы, т.к предлагает примитивное IDE с удобным управлением и отладкой и даже транслирует в много раз быстрее (чем все другие компиляторы). Причём объём выходного кода может достигать 64 кб, а с использованием оверлеев и больше. Кроме того, примеров исходников (причём реально полезных) программ на Турбо-Паскале я скачал ~480 файлов. И ещё ~250 файлов с примерами Паскаль-программ для других компиляторов Паскаля (в основном для МТ+). Учебной литературы по Турбо-Паскалю намного больше, чем по ретро Си (конечно по современным Си и особенно Си++ литературы тоже хватает, но по ретро Си - это лишь Керниган/Ритчи и ещё пара учебных книг из начала 80-тых). И важное удобство - это совместимость с TP для MSDOS и Windows XP. Что позволяет написать и отладить программу в кодах x86 для MSDOS, а потом странслировать с помощью TP 3.0 для CP/M. При этом можно использовать антикварную версию TP 3.02 для MSDOS из 1985 года (чтобы не соблазняли более поздние расширения языка). Эту версию (и даже версию 1.0) не проблема скачать (а версию 2.0 я не нашёл). Конечно, прибегнуть к TP 3.0 для MSDOS можно лишь при написании и отладке корректных программ с чисто текстовым выводом и корректным опросом клавиш. Не получится отладить программу, что напрямую лезет в экран РК86 или сканирует клавиатурную матрицу 8*8 через порты ППА. Но такие корректные к DOS программы не лезущие к железу, как, например, текстов редактор, корректный Нортон, свой ассемблер, мелкие программки-конверторы вполне можно писать и отлаживать в TP для MSDOS. С Си так сделать не получится. По крайней мере с Си, что для CP/M из 70-тых и 80-тых (их имеется до десятка). В корректных программах, если они не чисто текстово-системные (типа трансляторов, конверторов) нужно управление курсором и инверсией знакомест. Во-первых, можно использовать TSR драйвер VT52.SYS для MSDOS. Но даже и без него проблема легко решаема. Относительно позиционирования курсора, в MSDOS-версии TP есть процедура gotoXY. Для 8-ми разрядки не проблема её нацарапать на INLINE-ассемблере: или на самом Паскале (что тормознее): В последнем примере делается оконный вывод (без оконности программы с открывающимися окнами делать неудобно, программист сам должен отслеживать экранные позиции). Задаётся окно координатами углов прямоугольника WX1,WY1 и WX2,WY2 и задав окно (соответствующей функцией WinOpen), все последующие выводы символов попадают в окно. В вышеприведённых INLINE вставках код $D7 это RST 10. Это быстрый вызов п/п SCOUTA (save regs and CONOUT from reg.A). В моих версиях CP/M активно используются RST (что большое упущение Гарри Килэлла). Они всегда есть, блок RST кидается на адреса $08...$3A по горячему рестарту CP/M (и других DOS). А если мне надо странслировать мою программу для любой CP/M, то я прилинковываю загрузку блока RST в конец этой программы, тогда блок RST закидывается в ОЗУ $0008 при старте программы. Можно сделать COUT и средствами CP/M: Я ещё не использовал TP 3.02 для MSDOS, лишь скачал. Пока пишу крошечные учебные программки (чтобы вспомнить TP) и транслирую их TP 3.0 для CP/M под TSR-эмулятором, причём и CP/M-эмулятор запускать приходится под DosBox и на крошечной партиции (в 30 мб). В эмуляторе поддерживаются упр.коды консоли VT52 (можно H19, это терминал H19, он в основном совместимый с VT52). Если не считать ANSI.SYS, то на IBM PC не принято управлять курсором и цветом или инверсией искейп-кодами. В 90-тые годы я пробовал драйвер ANSI.SYS для MSDOS. Перетранслировав под экранные ANSI-коды (и IBM PC коды курсорных клавиш) CP/M-исходник текстового редактора MSDOS версией TP 7.1 в коды x86, получалась возможность эту CP/M-программу с позиционированием курсора и инверсией знакомест использовать в MSDOS (включив загрузку ANSI.SYS в CONFIG.SYS). Но особого смысла в этом нет, т.к в TP 7.1 есть свои функции управления курсором и цветом. Также можно все функции вывода на экран и опроса клавиатуры переписать с использованием INLINE-ассемблера 8086-го (там это намного проще) применяя вызовы функций MSDOS или IBM PC ROM-BIOS. Кроме того, можно встраивать в CP/M-программу свой драйвер клавиатуры и вывода на экран в граф.режимах, совмеcтимый с VT52 (я такой драйвер написал в середине 90-тых для всех графических SVGA режимов). Можно и не связываться с малоудобным ретро TP 3.0 для MSDOS, а сразу использовать более свежий TP 7.1 с модулем совместимости TP3. Насчёт отладки графических программ есть лишь идея. Хотя, в виду широкого наличия эмуляторов 8-ми разрядок это не необходимо, используя альтернативный BGI для VESA-режимов, можно прямо на PC отлаживать графический вывод, полностью аналогично реальной 8-ми разрядке (только не напрямую работая с экранным ОЗУ, а функциями). Это позволит даже программу работающую по экрану 8-ми разрядки отладить на PC. А вообще выбор Паскалей для 8-ми разрядки есть, хотя кросс-компиляторов для MSDOS (по крайней мере бесплатно) вообще нет. Кроме TP 3.0 мне удалось скачать (и на почти всех из них даже убедиться, что в них Hello World транслируется), следующие компиляторы: Pascal-МТ+, UCSD-Pascal, HiSoft-Pascal, JRT-Pascal, M-Pascal, Pascal/Z, Pascal-P и даже компилятор PL/0. PL/0 это сильно упрощённый Паскаль. Его придумал Н.Вирт, вероятно, чтобы показать как писать компилятор Паскаля на самом Паскале. Вряд-ли остальные Паскали лучше, чем TP от Borland. К ним стОит присмотреться лишь в случае, если нужен код под КР580, хотя для некоторых Паскалей, (точнее кроме МТ+, JRT и UCSD), практически нет документации. К тому же неудобство встраивания ассемблерных участков в Турбо-Паскаль теперь существенно сокращается, благодаря тому, что мне удалось найти программу создающая готовые INLINE-вставки прямо из REL-файла. Она написана на самом Турбо-Паскале и исходно была в виде картинок (сканов с бумажного носителя). Я потратил ~10 часов на её редактирование. REL --> INLINE конвертор | | | | Code: {********************************************************************} {* *} {* Pascal Link-80 version 3.2 *} {* *} {* Program to produce an INLINE-statement from an M80 REL-file *} {* *} {* Programmed in Turbo Pascal by J.A.C.G. van der Valk *} {* van Langendonckstraat 2 / 3076 SL Rotterdam *} {* *} {* Copyright 1986 by J.A.C.G. van der Valk *} {* *} {* Code is donated to public domain for non-commercial use only! *} {* It is prohibited to sell this code (or any part of it) to 3-rd *} {* parties; to use this code code for any commercial gain or to *} {* distribute any program (in any form), developed by use of the *} {* code in this file, on a commercial basis without prior written *} {* permission of the author. *} {* *} {********************************************************************}
const PrtOn=^P; PrtOff=^N; Bell=^G; LF=^J; CR=^M; UP=^K;
type symboltype = string[8]; fnamtype = string[20]; anystring = string[80]; hextype = string[2]; FileRecord = array [0..127] of byte; LoadItem = record typecode: byte; contents: byte; end; var FileBuffer: FileRecord;
count, { counter for lay out of .INC file } offset, { offset for PC-relative code } DataSize, { size of Data - segment } ProgSize, { size of Code - segment } ErrorCnt, { number of fatal errors detected } EntryCnt, { number of entry sumbols defined } Maxloc, { maximum for LC on the heap } LC, { Location Counter } Pbase, { Base for LC in Code Segment } Dbase, { Base for LC in Data Segment } Tsize, { Total Size of Code + Data } BitCnt, { Number of bit* read from .REL-file } ByteCnt, { Current Byte-number in .REL-file } ExtCnt, { Counter for external symbol storage } i: integer; { general purpose counter } STOP: boolean; { Flag for terminating REL-file reading } PrtOut: boolean; { Flag for shadow-output on printer } symbol: symboltype; { symbol in special link items } s: string[5]; { string for LC in inl-file } fnam: string[15]; { file - name of .REL file } WrkRec: LoadItem; { variable for storing temporary code } c: char; { dummy character } relfil: file; { .REL - file from M80 assembler } inlfil, { file for inline statement } fentry, { file for table of entry points } f: text; { file for listing on LST: or CON: } dsk: byte; { current disk number }
label 9999; { label to EXIT the program }
Procedure ClrEos; var i:byte; begin for i:=14 to 24 do begin gotoxy(1,i); clreol end; end;
Procedure Error(Errno: integer); begin write(Bell,'Error ',Errno:2,' ');
case Errno of
0: begin writeln('Unsupported Special Link item'); writeln('please consult the PL80-manual!'); end;
1: begin writeln('Code size exeeds available workspace'); writeln('Split source-code if possible!'); write('Continue anyhow (Y/N)? :'); read(kbd,c);if Upcase(c)='Y' then writeln('Y') else writeln('N'); Stop:=(Upcase(c)<>'Y'); end;
2: begin writeln('COMMON blocks are not supported'); writeln('please take notice of the PL80-manual!'); end;
3: begin writeln('Heap-overflow while linking'); writeln('Check ORG-statements in source-code!'); Stop:=true; end;
4: begin writeln('Heap-overflow on Chaining External : ',symbol); writeln('Split source-code if possible!'); stop:=true end;
end; { of case Errno}
ErrorCnt:=ErrorCnt+1; end;
procedure Directory(s:fnamtype); const extend = 12; setDMA = 26; searchFirst = 17; searchNext = 18; var FCB : array[0..32] of char; directorynamen : array[0..3,0..31] of char; drivename : char; dot,index,numlines,directorycode : integer; begin for index:=1 to length(s) do s[index]:=Upcase(s[lndex]); drivename:=chr(Bdos(25)+65); if s[2]=':' then begin drivename:=Upcase(s[1]); s:=copy(s,3,255); end; if Length(s)=0 then s:='*.*'; writeln('Drive : ',drivename); if (s[1]='*') and (s[2]='.') then s:='????????'+copy(s,2,255); dot:=Pos('.',s); if dot>0 then for index:=dot to 8 do Insert(' ',s,dot); if (s[9]='.') and (s[10]='*') then s:=copy(s,1,9)+'???'; if dot>0 then delete(s,9,1); for index:=Length(s) to 10 do s:=s+' '; FCB[0]:=chr(ord(drivename)-64); for index:=1 to 11 do FCB[index]:=s[index]; for index:=12 to 32 do FCB[index]:=chr(0);
bdos(setDMA,addr(directorynamen)); directorycode:=bdos(searchfirst,addr(FCB)); numlines:=0; if directorycode=255 then writeln('No files *.REL found');
while directorycode<255 do begin if directorynamen[directorycode,extend]=chr(0) then begin if numlines>0 then write(' 1 '); write(copy(directorynamen[directorycode],2,8), ^.^); for index:=9 to 11 do write(chr(127 and ord(directorynamen[directorycode,index]))); numlines:=(numlines+1)mod 5; if numlines=0 then writeln; end; directorycode:=bdos(searchnext); end; if numlines>0 then writeln; end;
procedure MemWrite(nn:integer;var WrkRec:LoadItem); begin if nn<=Maxloc then move(WrkRec,mem[nn*sizeof(LoadItem)+HeapPtr],sizeof(LoadItem)) else Error(3); end;
procedure MemRead(nn:integer;var WrkRec:LoadItem); begin if nn<= Maxloc then move(mem[nn*sizeof(LoadItem)+HeapPtr],WrkRec,sizeof(LoadItem)); end;
Procedure StoreExtrnl(symbol:symboltype); var WrkRec: LoadItem; i: byte; begin if (Tsize+(ExtCnt+1)*9) }= Maxloc then Error(4) else begin for i:=0 to 8 do begin WrkRec.typecode:=3; WrkRec.contents:=ord(symbol[i]); MemWrite((Tsize+ExtCnt*9+i),WrkRec); end; ExtCnt:=ExtCnt+1; end; end;
Procedure GetExtrnl(nn:integer;var symbol:symboltype); var WrkRec: LoadItem; i: byte; begin for i:=0 to 8 do begin MemRead((Tsize+ExtCnt*9+i),WrkRec); symbol[i]:=chr(WrkRec.contents); end; end;
Function Hex(b:byte): hextype; const HexTabl: string[16]='0123456789ABCDEF'; begin Hex:=HexTabl[(b shr 4)+1] + HexTabl[(b and $OF)+1]; end; procedure center(str:anystring); var i: integer; begin for i:=1 to (80-length(str)) div 2 do write(' '); writeln(str); end;
procedure conout(c:char); ' const Prt: boolean=false; begin if c=PrtOn then Prt:=true else if c=PrtOff then Prt:=false else begin Bios(3,ord(c)); if Prt and (c<>Bell) then Bios(4,ord(c)); end; end;
procedure init; var i: integer; dummy: real; begin BitCnt:=-1; ByteCnt:=-1; STOP:=False; ErrorCnti=0; EntryCnt:=0; ExtCnt:=0; count:=0; LC:=0; Pbase:=0; ProgSize:=-1; dummy:=memavail; if dummy < 0 then dummy:=dummy+65536.0; Maxloc:=Trunc(dummy/sizeof(LoadItem))-1; conoutptr:=addr(conout);
{ Hello messages } ClrScr; writeln; center(' P A S C A L L I N K - 80 '); writeln; center('version: 3.2 / dd:17-07-1986'); center('Programmed by J.A.C.G. van der Valk'); center('van Langendonckstraat 2 / 3076 SL Rotterdam'); center('Phone : 010 - 4320625'); writeln; center('(c) 1986 by FalconSoft (tm)'); writeln(LF); writeln('Free work space : ',Maxloc:5,' bytes'); writeln; Directory('*.rel');
dsk:=Bdos(25); { get current disk } { Input requests } writeln('Hit <RETURN> to toggle default drive (A/B)',LF); write('Name of .REL - file to convert 7 : '); readln(fnam); for i:=1 to length(fnam) do fnam[i]:=Upcase(fnam[i]); i:=Pos('.',fnam); if i>0 then delete(fnam,i,255); if (fnam[2]=':') and (Length(fnam)=2) then begin dsk:=ord(fnam[1])-65; if dsk in [0,1] then Bdos(14,dsk); fnam:=' '; end;
{$I-} assign(relfil,fnam+'.REL'); reset(relfil); while IOresult<>0 do begin ClrEol; if (fnam[2]=':') and (Length(fnam)>0) then begin dsk:=ord(fnam[1])-65; if dsk in[0,1] then Bdos(14,d*k); fnam:=copy(fnam,3,255); end else if length(Fnam)=0 then begin if dsk=1 then dsk:=0 else dsk:=1; Bdos(14,dsk); ClrEos; gotoxy(1,14); Directory('*.rel'); writeln('Hit <RETURN> to toggle default drive (A/B)',LF); write('Name of .REL - file to convert ? : '); readln(fnam); ClrEol; end else begin ClrEos; gotoxy(1,14); Directory('*.rel'); writeln('Hit <RETURN> to toggle default drive (A/B)',LF,LF); write(Bell,'File ',chr(dsk+65),':',fnam,'.REL not found',CR,UP); write('Name of .REL - file to convert 7 : '); ClrEol; readln(fnam); end;
for i:=1 to length(fnam) do fnam[i]:*Upcase(fnam[i]); i:=Po$('.',fnam); if i>0 then delete(fnam,i,255); if (fnam[2]=':') and (Length(fnam)=2) then begin dsk:=ord(fnam[1])-65; if dsk in[0,1] then Bdos(14,dsk); fnam:=' '; end; assign(relfil,fnam+'.REL'); reset(relfil); end; {$I+} if fnam[2]=':' then fnam:=copy(fnam,3,255); { delete drive specification if still present } ClrEol; Bdos(13); { reset disk system } Bdos(14,dsk); { restore previous default drive } write('Send output to LST: device also (Y/N)? :'); read(kbd,c); if Upcase(c)='Y' then writeln('Y',PrtOn) else writeln('N'); writeln; end; function getbit: byte; var mask: byte; begin BitCnt:=(BitCnt+1) mod 8; if BitCnt=0 then ByteCnt:=(ByteCnt+1) mod 128; if ByteCnt+BitCnt=0 then begin BlockRead(RelFil,FileBuffer,1); end; Mask:=128 shr BitCnt; if (FileBuffer[Bytecnt] and mask)=0 then Getbit:=0 else Getbit:=1; end;
function RelCode:byte; begin relcode:=(GetBit shl 1) + GetBit; end;
function CtrlField:integer; begin CtrlField:=(GetBit shl 3) + (GetBit shl 2) + (GetBit shl 1) + GetBit; end;
function GetByte:byte; var i,B: byte; begin B:=0; for i:=7 downto 0 do B:=B+(GetBit shl i); Getbyte:*B; end; function GetInteger:integer; begin GetInteger:=GetByte + Swap(GetByte); end;
Procedure GetAfield(var yy,nn:integer); begin yy:=RelCode; nn:=GetInteger; end;
Procedure GetBfield(var symbol:symboltype); var i,zzz:byte; begin zzz:=GetBit*4+GetBit*2+GetBit; if zzz=0 then zzz:=8; symbol:=''; for i:=1 to zzz do symbol:=symbol+chr(GetByte); end;
Procedure LoadByte; begin WrkRec.typecode:=0; WrkRec.contents:=GetByte; MemWrite(LC,WrkRec); LC:=LC+1; end;
Procedure LoadProgRel; var x:integer; begin x:=GetInteger+Pbase; WrkRec.typecode:=1; WrkRec.contents:=lo(x); MemWrite(LC,WrkRec); WrkRec.contenta:=hi(x); MemWrite(LC+1,WrkRec); LC:=LC+2; end;
Procedure LoadDataRel; var x:integer; begin x:=GetInteger+Dbase; WrkRec.typecode:=1; WrkRec,contents:=lo(x); MemWrite(LC,WrkRec); WrkRec.contents:=hi(x); MemWrite(LC+1,WrkRec); LC:=LC+2; end;
Procedure LoadCommRel; var x:integer; begin x:=GetInteqer; LC:=LC+2; end;
Procedure SpecialLink; var x,yy,nn,nextloc,nextcode: integer; begin Case CtrlField of 0: begin GetBfield(symbol); writeln('Entry symbol --> ',symbol); end; 1: begin GetBfield(symbol); writeln('Select COMMON block --> ',symbol); Error(2); end; 2: begin GetBfield(symbol); writeln('Program Name -> ',symbol); end; 3: begin GetBfield(symbol); writeln('Request iibrary search --> ',symbol); Error(0); end; 4: begin GetBfield(symbol); writeln('Extension Link item --> ',symbol); Error(0);stop:=true; end; 5: begin GetAfield(yy,nn); GetBfield(symbol); writeln('Define COMMON size -> ',symbol); writeln('$',Hex(yy),' $',Hex(hi(nn)),Hex(lo(nn))); Error(0); end; 6: begin GetAfield(yy,nn); GetBfield(symbol); writeln('Chain External --> ',symbol); case yy of 1: nn:=nn+Pbase; 2: nn:=nn+Dbase; 3: begin nn:=nn+Tsize; Error(2); end; end; { of case } MemRead(nn,WrkRec); writeln('loc = $',Hex(hi(nn)),Hex(lo(nn))); nextloc:=nn; nextcode:=yy; if nn+yy=0 then writeln('WARNING: Chain is empty, inspect source code!'); while nextcode+nextloc>0 do begin nextloc:=WrkRec.contents; nextcode:=WrkRec.typecode; WrkRec.contents:=lo(ExtCnt); WrkRec.typecode:=2; MemWrite(nn,WrkRec); MemRead(nn+i,WrkRec); nextloc:=nextloc+swap(WrkRec.contents); if nextcode+nextloc>0 then writeln('loc = $',hex(hi(nextloc)),hex(lo(nextloc))); WrkRec.typecode:=2; WrkRec.contents:=hi(ExtCnt); MemWrite(nn+1,WrkRec); nn:=nextloc; MemRead(nn,WrkRec); end; StoreExtrnl(symbol); end; 7: begin GetAfield(yy,nn);GetBfield(symbol); writeln('Define Entry point --> ',symbol); writeln('$',Hex(yy),' $',Hex(hi(nn)),Hex(lo(nn))); case yy of 1:nn:=nn+Pbase; 2:nn:=nn+dbase; 3:begin ErrorCnt:=ErrorCnt+1; writeln('Error: entry in common block'); end; end; { of case } if EntryCnt=0 then begin assign(fentry,fnam+'.ENT'); rewrite(fentry); writeln(fentry,'Table of Entry symbols '); writeln(fentry,'corresponding to ',fnam,'.INL'); writeln(fentry); end; write(fentry,symbol); for x:=1 to 8-length(symbol) do write(fentry,' '); writeln(fentry,' = $',hex(hi(nn)),Hex(lo(nn))); EntryCnt:= EntryCnt+1; end; 8: begin getAfield(yy,nn); write('External - offset -> '); writeln('$',Hex(yy),' $',Hex(hi(nn)),Hex(lo(nn))); Error(0); end; 9: begin getAfield(yy,nn); write('External + offset --> '); writeln('$',Hex(yy),' $',Hex(hi(nn)),Hex(lo(nn))); Error(0); end; 1O: begin getAfield(yy,nn); write('Define Size of DATA area --> '); writeln('$',Hex(hi(nn)),Hex(lo(nn))); DataSize:=nn; end; 11: begin GetAfield(yy,nn); write('Set Loading LC to '); case yy of 1: write('CSEG'); 2: write('DSEG'); 3: write('COMM'); end; { of case } writeln(' --> $',Hex(hi(nn)),Hex(lo(nn))); case yy of 1: LC:=Pbase+nn; 2: LC:=Dbase+nn; 3: LC:=Tsize+nn; end; { of case } end; 12: begin getAfield(yy,nn); write('Chain Address --> '); writeln('$',Hex(yy),' $',Hex(hi(nn)),Hex(lo(nn))); nextloc:=nn; nextcode:=yy; while nextcode+nextloc>0 do begin nextloc:=WrkRec.contents; WrkRec.contents:=LC; nextcode:=WrkRec.typecode; MemWrite(nn,WrkRec); MemRead(nn+1,WrkRec); nextloc:=nextloc+swap(WrkRec.contents); writeln('loc = $',hex(hi(nextloc)),hex(lo(nextloc))); WrkRec.contents:=LC; MemWrite(nn+1,WrkRec); nn:=nextloc; MemRead(nn,WrkRec); end; end; 13: begin getAfield(yy,ProgSize); writeln('Define PROGRAM Size --> $', Hex(hi(ProgSize)),Hex(lo(ProgSize))); Dbase:=ProgSize+3; { create space for JP ENDofDSEG } Tsize:=ProgSize+DataSize; if datasize>0 then tsize:=tsize+3; if Tsize}Maxloc then Error(1) else begin WrkRec.typecode:=0; WrkRec.contents:=0; for x:=0 to Tsize-1 do MemWrite(x,WrkRec); { Block-Data space (DS) initialized to null-bytes } end; end; 14: begin getAfield(yy,nn); writeln('END of PROGRAM'); Bitcnt:=-1; { forces to next byte boundary } end; 15: begin Stop:=true; writeln('END of FILE'); end; end; { of case }
end;
begin { main program } init; While not STOP do begin if Getbit=0 then loadbyte else begin case relcode of 0:SpecialLink; 1:LoadProgRel; 2:LoadDataRel; 3:LoadCommRel; end; { of case } end; end;
writeln(LF,LF,'diagnostics for linkage of file ',fnam,'.REL',LF); writeln('Free workspace available : ',Maxloc:5,' bytes'); if Tsize<Maxloc then writeln('Total workspace used : ',(Tsize+ExtCnt*9):5,' bytes') else writeln('Code size exeeds work space'); writeln('Total code size : ',Tsize:5,' bytes'); writeln('Code segment size : ',Progsize:5,' bytes'); writeln('Data segment size : ',Datasize:5,' bytes'); if Tsize < Maxloc then begin if ExtCnt > 0 then writeln('number of externals : ',ExtCnt:5) else writeln('no externals used'); if EntryCnt > 0 then writeln('number of entry-points : ',EntryCnt:5) else writeln('no entry points defined'); end;
LC:=0; if EntryCnt>0 then close(fentry);
if ErrorCnt>0 then begin writeln(f); writeln(ErrorCnt:3,' fatal error(s) detected',LF); write('make inline-file anyway (Y/N)? '); read(kbd,c); if Upcase(c)<>'Y' then begin writeln('N'); if EntryCnt>0 then erase(fentry); goto 9999; end else writeln('Y'); end else writeln('no fatal errors detected'); writeln(PrtOff); writeln('creating ',fnam,'.INL...'); write('Bytes written : ',#27,'.0'); assign(inlfil,fnam+'.INL'); rewrite(inlfil); write(inlfil,' INLINE({00000} '); while LC < Tsize do begin MemRead(LC,WrkRec); case WrkRec.typecode of 0: begin write(inlfil,'$',Hex(WrkRec.contents)); LC:=LC+1; end; 1: begin offset:=WrkRec.contents; MemRead(LC+1,WrkRec); offset:=offset+swap(WrkRec.contents)-LC; if offset>0 then write(inlfil,'*+',offset) else write(inlfil,'*',offset); LC:=LC+2; end; 2: begin ExtCnt:=WrkRec.contents; MemRead(LC+1,WrkRec); ExtCnt:=ExtCnt+swap(WrkRec.contents); GetExtrnl(ExtCnt,symbol); write(inlfil,symbol); LC:=LC+2; end; else begin write('Internal error, program aborted'); close(inlfil);erase(inlfil); goto 9999; end; end; { of case } if LC < Tsize then write(inlfil,'/') else writeln(inlfil,');'); if (LC=ProgSize) and (Datasize>0) then begin writeln(inlfil,'$C3/*+',Tsize-LC-1,'/'); LC:=LC+3; writeln(inlfil,' { start of DATA segment }'); Str(LC,s); for i:=length(s) to 4 do s:='0'+s; write(inlfil,' {',s,'} '); count:=0; end; count:=(count+1) mod 10; if count=0 then begin writeln(inlfil); Str(LC,s);for i:=length(s) to 4 do s:='0'+s; write(inlfil,' {',s,'} '); end; write(LC:5,^H,^H,^H,^H,^H); end; close(inlfil);
9999: writeln(#27,'.2'); close(relfil); end.
| | | | |
Что касается компиляторов Си. Я имел дело только с древними Си для КР580 из начала 80-тых, - BDS и AZTEC. И по-моему они уступают Турбо-Паскалю по качеству кода. Недавно я скачал и более свежие компиляторы HiTech C и SDCC (и ещё до десятка менее солидных). Естественно предположить, что более поздние разработки, тем более в виде кросс-программ с оптимизацией могут давать плотность кода не уступающую Турбо-Паскалю (но сам я это не видел, это лишь логичное предположение исходя из большей популярности этих компиляторов). Документация на них также хорошая. Может и ещё какие-то прогрессивные компиляторы есть. Было бы интересано, если бы программирующие на этих Си сообщили об их эффективности. Я сам вряд-ли займусь Си для 8-ми разрядки. PL/M намного эффективнее, но разработка на нём длится на порядок (а то и два) дольше, чем на Турбо-Паскале и в разы дольше, чем на Си. И освоение PL/M, несмотря на его предельную простоту, отнимает больше времени, чем Си или Паскаля. Да и практически нет учебников и (для PLMX) совсем нет примеров чужих исходников. А если уж необходима эффективность, то похоже, лучше начать с PLI от Digital Research. И для процессора КР580 PL/M и PL/I остаются вполне разумным вариантом.
Last edited by barsik on 07 Nov 2019 04:09, edited 1 time in total.
|
06 Nov 2019 23:40 |
|
|
Alekcandr
Doomed
Joined: 01 Oct 2007 10:30 Posts: 665 Location: Ukraine
|
barsik, я не знаю, как вы относитесь к Си. Но все выше сказанное можно переложить на HI-TECH Z80 C Compiler. Возможностей и гибкости у него будет побольше Paskal-я.
И да. Есть возможность у их него ассемблера jp заменять на jr. Где это возможно. Оптимизация кода так сказать.
_________________Эмулятор OrionEXT:
http://www.orion-ext.narod.ru
|
07 Nov 2019 06:27 |
|
|
barsik
Doomed
Joined: 19 Feb 2017 03:46 Posts: 583 Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
|
Нормально. Он мне даже нравится и писать на нём мне пришлось заметно больше, чем на Паскале. Сложные многоэтажные конструкции понятнее на Си и с указателями в Паскале хуже (они используются в TP лишь для управления динамической памятью). В CP/M я долго решал, что лучше BDS или AZTEC, в итоге использовал и тот и другой (а в литературе больше упоминаний про BDS и он удобнее для ассемблера). В MSDOS только Турбо-СИ (с Visual-C не смог разобраться, точнее не особо и хотел, т.к задач под него не было). У любого Си возможностей побольше, чем у Паскаля. Ещё даже CP/M-версия HiTeсh 3.09 выпущенная в октябре 1989 (30 лет) производит хорошее впечатление. Это намного позднее, чем конец 70-тых, когда появились первые версии Паскалей для 8080 (а видимо, чем компилятор свежее, тем лучше). И документация производит хорошее впечатление. А уж кросс-компилятор наверняка ещё лучше (не знаю, когда его бросили сопровождать и дорабатывать, но явно позднее). На форумах только и встречаются пользователи его, SDCC, ZDK88 и ещё IAR (но его трудно скачать и он платный). А ретро CP/M-версиями Си для КР580 из 1981 уже, похоже, никто не пользуется. Интересно, надо будет взглянуть, вдруг этот ассемблер можно использовать отдельно для трансляции программ вручную написанных на ассемблере.
Last edited by barsik on 07 Nov 2019 07:24, edited 1 time in total.
|
07 Nov 2019 06:57 |
|
|
Alekcandr
Doomed
Joined: 01 Oct 2007 10:30 Posts: 665 Location: Ukraine
|
Это ассемблер обладает всеми необходимыми атрибутами. Но в силу внутреннего стандарта фирмы HiTeсh может вам показаться в чем то не обычным по равнению с классикой от меломягких. Контора HiTeсh поддерживала не вероятно количество процессоров 80г. Последний релиз для Z80 был сделан на PC 7.80PL2 2001г. Но как бывает в жизни, все не вечно. HiTeсh поглотил MicroChip (или наоборот ). Этот MicroChip относительно недавно сожрал Atmel
_________________Эмулятор OrionEXT:
http://www.orion-ext.narod.ru
|
07 Nov 2019 07:26 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 23468 Location: Silicon Valley
|
Вытаскиваю топик из небытия, освободив от лишних "лирических" отступлений...
|
04 Feb 2023 20:58 |
|
|
Pyk
Maniac
Joined: 21 Jan 2003 04:08 Posts: 242
|
Еще в прошлом году провел небольшое мини-исследование эффективности разных ЯВУ при генерации кода для i8080. Для примера была выбрана следующая задача на РК-86: на основании информации в ПЗУ экран заполняется "случайными" клетками и прогоняется 10 итераций по правилам игры "жизнь". Один и тот же алгоритм был реализован на ассемблере, Си (z88dk), с8080 (компилятор Си Алексея Морозова), PL/M, Millfork. Сильно оптимизировать алгоритм не старался, в конце для каждого языка пробовал сделать минимальную оптимизацию - разные типы для переменных в критических участках, альтернативные записи выражений и т.п. и выбирал лучший по быстродействию вариант.
Результаты не претендуют на достоверность во всех случаях, но иллюстрируют общую тенденцию. Прогонял еще некоторые тесты - результаты похожие.
_________________Эмулятор Emu80
Last edited by Pyk on 11 Jun 2024 13:18, edited 2 times in total.
|
11 Jun 2024 09:38 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 23468 Location: Silicon Valley
|
А кто такой Millfork? Надо на моём языке Robby попробовать Это который бывший RW1 из состава бывшего RW1P2 (теперь nedoPC SDK)…
|
11 Jun 2024 12:47 |
|
|
Pyk
Maniac
Joined: 21 Jan 2003 04:08 Posts: 242
|
_________________Эмулятор Emu80
|
11 Jun 2024 12:55 |
|
|
Who is online |
Users browsing this forum: No registered users and 2 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|