Использование ЯВУ для разработки программ для РК86

Перейти вниз

Использование ЯВУ для разработки программ для РК86

Сообщение  barsik в Чт Сен 07 2017, 15:07

Общеизвестно, что программирование на ассемблере очень неэффективно. В книге Эльфринга написано, что хороший программист в среднем за рабочий день создает всего 100 строк исходного текста (а программа объёмом в 14 кб имеет исходник в 7000 строк ассемблера). Понятно, что набрать в редакторе за один день можно и тысячи строк, только проблема в том, что это точно не будет работать.

Т.к программирование на ассемблере очень чревато ошибками, то требуется обязательно тестировать любую процедуру написанную на ассемблере. И в случае ошибки, приходится выяснять проблему путём долгой и кропотливой возни с отладчиком. Так что неэффективность ассемблера объясняется вовсе не сложностью хранить в голове конструкции, форматы данных, адреса и т.п (это-то как раз не проблема), а главным образом в том, что очень легко ошибиться, а найти потом эти ошибки сложно.

Поэтому и придумали языки высокого уровня (ЯВУ), т.к ЯВУ даже древний, намного более продуктивен, чем ассемблер. Впрочем, не надо обольщаться. Как написано в предисловии книги У.Морера, почти все коммерчески успешные программы для 8-ми разрядок были написаны именно на ассемблере, а на ЯВУ не было написано вообще ничего (кроме банковских программ на Коболе). Проблема в том, что 8-ми разрядки имеют мало памяти, а объём кода из под компилятора очень быстро нарастает с ростом сложности программы. Объём кода программы на ЯВУ в 4-8 раз больше, а скорость прогона во столько же ниже по сравнению с ассемблером.

Стоит заметить, что в литературе встречается утверждение, что есть один более эффективный, вероятно самый низкоуровневый ЯВУ - PL/M. Про который его автор Гарри Килэлл утверждал, что для больших программ он генерит даже более эффективный код, чем на ассемблере. Теоретически это невозможно, но реальные программы на ассемблере пишут живые люди, которые пишут не идеально (применяя повторные куски кода и неоптимальные алгоритмы), потому похоже, что автор PL/M был прав. Но для меня пока это остаётся непроверенным слухом и городской легендой, т.к CP/M-версии компилятора нет.

Но даже для критичных по скорости и объёму кода программ выход есть - это использование ЯВУ с заменой части процедур на ассемблер. Это удобно в BDS Си (не Aztec), а также в большинстве других ЯВУ (где используется линковка). Даже широко презираемый программистами бейсик имеет компиляторы с линковщиком, что позволяет встраивать в программу бейсика куски написанные на ассемблере без всяких ухищрений свойственных интерпретатору (за счёт загрузки кодов из операторов DATA).

Впрочем уверен, что для несложных по логике и графике программ, уровня XONIX, PACMAN даже возможностей голого ЯВУ без вкраплений ассемблера достаточно. Например, первый PACMAN для 8-ми разрядки вообще был написан на интерпретаторе бейсика, а это уж какая тормозятина.

Надеюсь, что вышенаписанного достаточно, чтобы показать, что использование ЯВУ при разработке ПО для РК86 имеет смысл. Но как это сделать, ведь компиляторы работают в CP/M и генерируют код рассчитанный для работы в CP/M? А нам требуется поиметь на выходе программу пригодную для запуска из среды монитора РК.

Из-за отсутствия в базовом РК86 входов BDOS и BIOS, в программе нельзя использовать стандартные функции ввода/вывода. Понятно, что программе рассчитанной для РК без CP/M не следует лезть к дискете. Но даже и без этого, т.к программы CP/M для ввода/вывода используют BDOS и BIOS, то странслированная традиционно программа всё-равно работать не будет.

Для решения проблемы есть несколько вариантов. Во-первых, можно сделать эмуляцию CP/M, а именно пристроить к программе эмулятор BDOS/BIOS. Во-вторых, можно переписать в компиляторе некоторые процедуры низкого уровня в RUN-тайм библиотеке. Исходник RUN-тайм библитеки у некоторых компиляторов (например BDS Си) включён в дистрибутив и их изменить несложно (переадресовав вызовы консольных функций BDOS на входы F812, F803 и F809).

В третьих, можно написать процедуры и функции на ассемблере делающие вызовы ПЗУ F800. Проще всего это делается в компиляторах, где есть оператор INLINE (или #ASM...#ENDASM). Компиляторы Паскаль МТ+, AZTEC и BDS Си это свойство имеют. Тогда тратится всего 3 минуты, чтобы вставив маш.коды команды CALL на стандартные входы ПЗУ, получить процедуры и функции для вызова из Паскаля подпрограмм ПЗУ РК86.

Т.е используя INLINE-ассемблер пишутся интерфейсные функции типа CONOUT, GETKEY, GOTOXY, MSSG, что и позволяет делать интерфейс с железом РК, не используя стандартные функции ЯВУ. Если же в ЯВУ нет встроенного ассемблера, можно это сделать внешними процедурами на ассемблере.

Эмулятор BDOS, BIOS это единственное решение для тех ЯВУ, где нет ассемблера и невозможно что-то изменить в RUN-тайм библиотеке. Программа транслируется для CP/M, используя стандартные процедуры. А затем к коду программы подключается блок эмулятора BDOS и BIOS, это всего 0.5 кб кода. Например, на Паскале МТ+ это сделать легко, т.к код странслированной программы в начале специально имеет 16 NOP-ов, куда пользователь может вставить свой код инициализации. Как выглядит эмулятор BDOS/BIOS можно посмотреть на программе DDT, что я адаптировал для РК86 от CP/M.

СИ даёт больше возможностей, генерит несколько более компактный и скоростной код, хотя Паскаль легче в освоении и симпатичен для тех, кто ранее имел дело с бейсиком. Тут для Z80 выбор есть, а вот для КР580 выбора практически нет. Есть всего три Паскаля выдающих код КР580, из них два - МТ Plus и JRT хорошо документированы. МТ+ довольно эффективный, хотя слышал мнение, что он даёт код более объёмный, чем BDS Си. Зато Паскаль проще и его учат в школе и ВУЗ-е. В любом случае код от компилятора прогоняется на порядок быстрее, чем в бейсик интерпретаторе.

В 80-тые годы, в эпоху популярности РК, ЯВУ были недоступны, но меня удивляет почему сейчас, когда всё это доступно и есть живые любители РК86, никто не начал использовать ЯВУ. Смысл этого поста в том, чтобы убедить фанатов РК, что даже с небольшими трудозатратами можно писать игры. Т.к написание игр для РК на ассемблере более трудоёмко, то использование ЯВУ это выход. Можно писать новые игры. Но есть смысл даже в переписывании старых игр РК86, потому что теперь можно использовать цвет (оцветить старые игры) и путём использования специального фонта с фигурками, состоящими из тайлов (например в матрице 2*2 знакоместа), получить визуально на экране полноценную графику, причём не имея скоростных ограничений, как это на графических ЭВМ, которые тормозят.

Надеюсь, что через полгода мне и самому удастся продолжить эту тему и показать, как можно написать XONIX или PACMAN на ЯВУ. Мучает только проблема выбора - Си, Паскаль, бейсик-компилятор или PL/M. Каждый из них имеет свои преимущества.

И нужен компилятор с хорошей документацией. Пока нашёл документацию по бейсикам-компиляторам CBASIC и BASCOM, Паскалю МТ+ и Aztec и BDS Си и, читая пытаюсь что-нибудь вспомнить, т.к уже ничего не помню и по уровню знаний равен полному чайнику в программировании на ЯВУ.

На первый взгляд кажется, что даже на бейсике компиляторе можно бы написать вполне приличное ПО для РК86, а скорость разработки будет даже выше. Но пока с бейсиком-компилятором некоторая неясность, - надо разобраться как делать ПО, работающее без ДОС, т.к обычно бейсики компиляторы генерируют не машинный, а промежуточный код и используют BRUN-модуль, из-за чего без ДОС работать вообще не могут, исходников RUN-тайм библиотеки нет и базовые процедуры не изменить. Кажется всё-же можно компоновать одномодульные CP/M-программы, и тогда за счёт вызова USR-подпрограмм можно выкрутиться, т.е делать программы на бейсике не использующие вызовов BDOS или BIOS CP/M.

Но вы не ждите когда я что-нибудь "рожу", вы можете легко сами сделать то же самое и начать делать игры для РК86 на каком нибудь ЯВУ. Для этого достаточно найти какую-нибудь среду, где работает любой из перечисленных выше компиляторов ЯВУ для CP/M. В простейшем случае - это эмулятор какого-нибудь компьютера, где есть CP/M и можно прогонять CP/M компиляторы. И проверять результирующий код для начала удобнее в эмуляторе РК86, т.к обычно очень хлопотно пересылать коды из IBM PC на реал.

Для использования корректных CP/M-программ, таких как большинство компиляторов, удобнее всего пользоваться резидентными эмуляторами CP/M, типа 22NICE. Они позволяют запускать программы CP/M в MSDOS и Windows XP так, как будто это программы для IBM PC (в более новых Windows - нет, там нет поддержки MSDOS). Благодаря BAT-файлам получается очень удобно, хлоп по кнопке и всё транслируется и через секунду Вы уже в эмуляторе, проверяете результат. Такого удобства никогда не получить используя компиляцию на реальной ЭВМ (даже если бы в РК для этого хватало ОЗУ и скорости).

Кстати, собственно нехватка ОЗУ в РК уже не стоит остро, как это было в 80-тые, т.к даже для программы написанной на ЯВУ объёма 29 кб для загрузки кода и, для РК с совместимым расширением ОЗУ, ещё 15 кб в области 8400...BFFF) под буфер данных вполне достаточно. В случае необходимости  несложно доработать РК, чтобы иметь сплошное ОЗУ в 48К. Кстати, раз уж сплошные 48К по принципу Микроши получить легко, то почему нет? Например, CP/M-Нортон написанный на BDS Си занимает 32 кб. А игры написанные на ЯВУ и содержащие много лабиринтов уж точно превысят размер в 29 кб.

Интересно, что загрузка программ размера большего чем 29 кб с магнитофона в системе, где ОЗУ расширено в окне A000...BFFF (тогда ОЗУ занимает два несмежные участка), должна происходить в три этапа. Для этого пишется стартёр, автоматически перехватывающий управление при загрузке по I, который и грузит два блока кодов в ОЗУ A000...BFFF и 0...7600. Стартёр перехватывает управление (за счёт загрузки кода в стек), выводит на 4 секунды мигающую надпись "Do not stop tape" и затем два блока считываются и стартуют.

Естественно, если ОЗУ сплошное в 48 кб, то загрузка программы размером более 29 кб и одним сплошным куском не проблема. При такой уже несовместимой по порту клавиатуры доработки, удобнее использовать двухрежимный монитор, который работает в двух режимах на 32 кб и на 48 кб (отличие прошивок только в адресе ППА клавиатуры и адресе экрана). Тогда в режиме 48 кб можно грузить большие программы под 45 кб одним блоком. Физически это достигается тем, что на РФ2 с ROM-BIOS напаивается второе ПЗУ РФ2. И, если в момент нажатия кнопки сброс удерживается клавиша <УС>, то стартует монитор на 48 кб. Это удобно, не требуется доп.управление и сохраняется совместимость. Два компьютера в одном флаконе.

Так как, в качестве языка программирования очень перспективен PL/M, прилагаю ссылку на подборку материалов по языку PL/M и по языку PL1 для программирования для КР580.

Для PL1 есть версия компилятора для CP/M. Хотя PL/M похож на PL1, про его эффективность ничего неизвестно. Точнее некоторые, начитавшись Википедии, PL1 ругают, но сомневаюсь, что для 8-ми разрядки стали бы писать неэффективный компилятор. Возможно он такой же тормозной как Си или Паскаль, но скорее всего, т.к основы языка те же, он также эффективен, как и PL/M. В любом случае для начала освоения PL/M он тоже годится, т.к в нём можно использовать только те операторы, что есть и в PL/M.


Последний раз редактировалось: barsik (Пн Ноя 05 2018, 02:18), всего редактировалось 2 раз(а)

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

Паскаль для разработки программ РК86

Сообщение  barsik в Пт Апр 27 2018, 14:46

Дилемма Паскаль или Си отравила жизнь многим дилетантам в программировании. В 90-тые, пытаясь освоить программирование, я постоянно разрывался, занимался и тем и этим. В итоге метания между компилятором BASIC-а, Си и Паскалем сильно мне вредили. В итоге неэффективно потрачено время. Лучше не повторять этой ошибки и строго заниматься только СИ или только Паскалем.

После долгого обдумывания, для программирования для РК86 выбираю Паскаль. Хотя на Си я программировал больше, и Си считается лучшим. Пусть Си популярнее, а компиляторы Си для 8-ми разрядки эффективнее и возможности Си выше, но для меня бОльшее значение имеет приятность программирования, да и бОльшая лёгкость в освоении тоже важна, т.к я очень давно не программировал и из-за возраста "входное сопротивление" моего мозга резко повысилось. Мне важнее более приятный процесс, чем более качественный результат.

Далее необходимо сделать выбор компилятора. Для КР580 есть 4 альтернативы. Это три компилятора для CP/M и родной компилятор для РК86 (работающий без ДОС). Для начала, чтобы убедиться в их работоспособности попробовал написать Hello World на всех этих компиляторах. Эти компиляторы я сложил здесь. Если у Вас есть другой Паскаль, что компилирует в код для КР580, дайте знать. Вроде бы были популярны PascalP и USCD Pascal, оба использующие P-код, но пока их не нашёл.

Родной компилятор Паскаля для РК86 у меня есть, но сейчас он недоступен, т.к остался на архивных дискетах RK-DOS, которые не считать на PC. Это не важно, т.к этот компилятор сразу-же отпадает по следующим причинам.

Для РК86 есть Паскаль от Микроши (а также Best-Си и Фортран). Но этот Паскаль не полноценный компилятор, т.к вместо маленькой RUN-тайм библиотеки, использует в качестве неё сам компилятор, отчего объём странслированной программы увеличивается не на 1-2 кб библиотеки времени исполнения, а разбухает аж на целых 16 кб кода всего компилятора. Что делает этот Паскаль пригодным лишь для знакомства с Паскалем.

Трансляция происходит сначала в текст на ассемблере, который затем ассемблируется ассемблером МИКРОН. Это приводит к тому, что в ОЗУ РК должны одновременно умещаться: текстов редактор (2 кб), компилятор Паскаля, компилятор ассемблера (2 кб), исходный текст на Паскале, промежуточный текст на ассемблере и результирующий объектный код странслированной программы. С учётом мизерности размера ОЗУ в РК максимальный размер программы ограничен в 200 строк Паскаля. То же самое относится и РК-компилятору Best-Си, хотя на ОРИОНЕ на нём писали игры.

На других отечественных компьютерах с бОльшим ОЗУ (Партнёр и Вектор) с успехом использовался ЛС-паскаль, который использует P-код. Но увы, его версий для РК или ОРИОНА не встречал.

Один из трёх CP/M компиляторов Паскаля для КР580 также отпал, т.к мне не удалось странслировать на нём Hello World. Разобраться не смог, т.к к этому Паскалю нет никакой документации.

Из 4-х попробованных версий JRT-паскаля одна (3.0) оказалась дохлой. JRT-паскаль оказался очень эфективным по объёму кода (код Hello World всего 32 байта) и документация к нему хорошая. Но увы, этот компилятор транслирует не в COM-файл, а в INT-файл, для запуска которого используется программа EXEC.COM размером в 24 кб. Это не годится не только из-за огромного размера кода, а то, что без CP/M программа EXEC не может загрузить INT-файл и следовательно это не удастся использовать на РК86 без CP/M. Жалко, что JR-Паскаль не годится, т.к в нём есть многие вещи, что отсутствуют в МТ+.

В итоге, как ни крути, остался всего один компилятор Паскаля, который позволяет транслировать программы для РК86. Это Паскаль МТ+. Дополнительным плюсом МТ+ является наличие отладчика, возможность анализа ассемблерного кода полученного от каждого оператора Паскаля и возможность трансляции кода на любые адреса и для ПЗУ. Hello World транслируется в программу объёмом в 4 кб (использование ключа $K позволяет исключить ненужные стандартные операторы из кода, что сокращает объём, но пока в этом не разбирался).).

Есть 3 версии компилятора MT+. Есть версия 5.5 от фирмы MicroSystems и есть DR версия 5.5 от Digital Research (она отличается наличием SPP, пакета для оптимизации по скорости и анализатора) и есть DR версия 5.6. Компилятор МТ+ отлично документирован и экспертами признавался намного более мощным и профессиональным инструментом, чем борландовский ТP 3.0, хотя TP благодаря IDE и позволяет значительно ускорить разработку и отладку программ. Но увы, TP не генерит код для КР580, только для Z80. Пока для освоения Паскаля использую самую древнюю версию MT+ от СМ-1800, т.к она русифицирована, а ускоритель SPP пока не нужен.

Для получения кода пригодного для РК86, несложно переписать в RUN-тайм библиотеке процедуру @BDOS так, чтобы в соответствии с передаваемым номером функции происходил вызов ПЗУ F800 по адресу высчитанному по формуле (F800 + func*3). Но это мне пока не надо, т.к отлаживать удобнее на CP/M-машине (точнее в эмуляторе ОРИОНА с CP/M). Малый объём ОЗУ РК не проблема (пока сама странслированная программа умещается в ОЗУ), т.к ключ $Z позволяет задать RAMTOP. Ограничения Паскаля по скорости можно устранить вкраплениями ассемблера (это также немного сокращает объём кода).

Чтобы получить программу в кодах КР580 работающую на РК86 без наличия на ней CP/M, надо решить проблему ввода/вывод, т.к ввод/вывод самого компилятора рассчитан на CP/M, т.е наличие в ОЗУ входа в CP/M BDOS и входов в CP/M-BIOS. Проще всего встроить вызовы ПЗУ F800 в виде INLINE-процедур написанных на ассемблере. Встроенный в MT+ ассемблер позволяет прямо в Паскаль программу встраивать куски на ассемблере (и даже двумя способами - в виде HEX-дампов и в виде мнемоник ассемблера).

Нужные для программирования РК простейшие процедуры COUT, MSSG, GOTOXY, XF81B, CONIN, RD_SCR... я написал в INLINE ассемблере в мнемониках КР580 быстро. К сожалению, INLINE ассемблер использует маловразумительные мнемоники КР580, а не всем понятные мнемоники Z80. Потому большие вкрапления ассемблера удобнее делать в виде прилинковываемого внешнего ассемблерного модуля, который можно писать в мнемониках Z80 (естественно избегая команд, которых в КР580 нет).

Не освоив полностью сам Паскаль, нет смысла сразу начинать писать игру для РК86. Сначала разумнее пописать простенькие программки и полностью разобраться в работе всех операторов, стандартных процедур и функций. Затем надо разработать "движок", т.е набор подпрограмм позволяющий выводить движущиеся динамически меняющиеся спрайты (спрайты состоящие из тайлов-символов). Но предварительно надо написать редактор спрайтов, позволяющий нарисовать 8 фаз спрайта и посмотреть как они выводятся на экран в динамике.

Конечно редактор спрайтов удобнее сделать программой для PC, т.к сам РК86 не позволяет загружать тайлы спрайтов в фонт. Но я (для тренировки) собираюсь написать на Паскале редактор спрайтов именно для РК86 в графическом режиме 128*129 (который требует альтернативный фонт, который как раз есть у меня на реале и поддержан в моём эмуляторе РК86 на ОРИОНЕ).

Ближайшее время (несколько недель) для освоения Паскаля буду писать простенькие программки. Пока на второй день изучения странслировал несколько программок уровня Hello World (чтобы освоить интерефейс с подпрограммами ПЗУ РК) и начал писать нечто вроде монитора РК86 на Паскале (не трудно сделать и с записью на МГ). Пока написал просмотр дампа. Это пригодится для отладки Паскаль программ. Добавлю к этому еще мини дизассемблер, это позволит удобно отлаживать. Для начала хочу понять насколько быстро разбухает код Паскаля.

Затем собираюсь написать текстов редактор, а потом, возможно, и ассемблер в мнемониках Z80. Может быть ещё что-нибудь простенькое, типа микро-ОС для ROM-диска или прошивателя ПЗУ 573 РФ2 или 556 РТ4. И только затем, набравшись немного опыта в Паскале, возьмусь за разработку редактора спрайтов и графических процедур для их динамического вывода.

И только потом можно будет начать делать игры для РК86 с тайловой графикой. Надеюсь к тому времени появится эмулятор РК86 с коммутируемыми фонтами. Без коммутации фонтов, т.е без минимальной графики, вообще нет смысла писать игры для РК86, т.к игры с бегающими по экрану ASCII-символами это просто уродство, которого достаточно написано ещё в 80-тые.

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

.

Сообщение  barsik в Ср Май 02 2018, 19:16

Недавно разбирался с основами программирования на МТ-Паскале. Сам Паскаль работает великолепно, всё как в учебнике, да и находить и устранять ошибки удобно, диагностика достаточная. Но вот с ассемблером не всё оказалось так радужно, как казалось по документации.

С ассемблером при программировании для РК обязательно приходится разбираться, т.к стандартные процедуры Паскаля для ввода/вывода недоступны (т.к они работают через CP/M, которая в ОЗУ РК отсутствует). Таким образом программа для РК86, написанная на Паскале, без вкраплений ассемблера вообще ничего не может.

Например, если хочется напечатать на экране значение переменной в десятичном виде, то облом. Без собственных процедур ввода/вывода это невозможно - попытка использовать процедуры WRITE или WRITELN приводит к кошмарному улету. Потому всё, что программа может делать, это то, что могут делать подпрограммы ПЗУ РК86, причём их ещё надо как-то вызвать из программы на Паскале.

Потому для начала, используя INLINE вставки на ассемблере написал несколько простейших процедур для интерфейса с подпрограммами ПЗУ РК86. Например, вот самые простейшие:

Код:

procedure cout(SYM: char);
begin
   INLINE( "LDA/SYM /
           "MOV C,A/
           "CALL/$F809 );
end;

function conin: char;
var RESULT:     char;
begin
   INLINE( "CALL/$F803/
           "STA/RESULT );
   conin := RESULT;
end;

procedure mssg(stradr: ukchar);
begin
   INLINE( "LHLD/STRADR/
           "CALL/$F818 );
end;

procedure hex(byt: byte);
begin
   INLINE( "LDA/byt /
           "CALL/$F815 );
end;

{------------------------------------------------}

procedure print(str: string);
label 10;
var    adr: integer;
        len: byte;
begin
    adr:=addr(str)+1; len:=sizeof(str);
10: inline( "LHLD/adr/
            "MOV C,M/
            "CALL/mcout/
            "INX H/
            "SHLD/adr/
            "LXI H/len/
            "DCR M );
  if len<>0 then goto 10;
end;

Уже эти простейшие процедуры позволяют тот же минимальный ввод/вывод, что есть в ПЗУ РК86. Например, можно выводить строки текста задав их такой же INLINE-вставкой с завершающим нулём. Вот программа Hello World для РК86.

Код:

prog HELLO_WORLD;
type    ukchar = ^char;
var     ukch: ukchar;

procedure mssg(text: ukchar);
begin
   INLINE( "LHLD/text/
           "CALL/$F818 );
end;

procedure thello;
begin
  INLINE ( $1F/10/'HELLO WORLD !'/0 );
end;

begin   (*  main  *)
        mssg(addr(thello));
  INLINE( "JMP/$F86C );
end.

А для вывода строк самого Паскаля процедура MSSG не годится, т.к в Паскале строки представлены иначе (для этого смотри процедуру print). Кстати, и вывести число в десятичном виде эти п/п-ммы всё-равно не позволяют (надо писать свою процедуру PR_DIG), т.к в ПЗУ РК не оказалось подпрограммы для вывода десятичных чисел. Но, если каким-либо образом "починить" работу оператора WRITE, то вывод десятичных чисел и строк станет возможным средствами самого Паскаля.

Как видите простейшие процедуры встроенный ассемблер позволяет написать. Но если попытаться написать чуть более сложную инлайн-процедуру на ассемблере, то выясняется, что никак не задать адрес переходов.

В документации указано, что тогда можно использовать идентификатор звёздочка (*), что заменяет текущий адрес трансляции (аналог $ в обычном ассемблере). Т.е можно писать "JMP/*+25/. Но увы, компилятор не воспринимает звездочку, даёт на неё ошибку. А с префиксом $ (что вообще-то HEX-префикс), т.е JMP/$*+25 транслируется, но если посмотреть полученный код отладчиком, то адрес перехода подставляется неверный.

Таким образом, я остался практически без INLINE-ассемблера. Без переходов можно написать только простейшие линейные процедуры. Выходом остаётся вставлять в процедуру INLINE-вставку в кодах, предварительно странслировав её обычным ассемблером прямо на рабочий адрес. Адрес размещения процедуры можно узнать из PRN-файла (используя прилагаемый дизассемблер Паскаля) или проще контроллируя код отладчиком. При этом эта INLINE-процедура должна стоять в начале исходника, чтобы при дальнейшем написании программы адрес процедуры уже не сдвигался. Тогда с помощью INLINE вставляется готовый дамп странслированной программы.

Но, т.к процедуру всё-равно надо транслировать обычным макроассемблером, то проще не вставлять её дампом (что вызывает необходимость контроллировать, чтобы адрес размещения совпал), а просто прилинковывать её при линковке, странслировав в виде внешнего модуля.

Да и вообще нашлось много нюансов, на которые пришлось потратить время. Например, надо писать не 'PUSH PSW', а 'PUSH P' , и не 'POP PSW', а 'POP PS', иначе возникает ошибка. Долго трахался с этим, а узнать это удалось только изучив дамп компилятора.

Но всё же надеюсь, что INLINE-ассемблер каким-то образом позволяет переходы. В документации на русском написано про звёздочку. Возможно переводная документация не соответствует, - от более раннней или более поздней версии. Видимо, надо читать читать документацию на английском, может быть там что-то найду.


Последний раз редактировалось: barsik (Чт Ноя 08 2018, 06:04), всего редактировалось 1 раз(а)

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

.

Сообщение  barsik в Сб Май 12 2018, 23:49

Нашёл ещё один пригодный для программирования для РК86 компилятор Паскаля. Скачать можно здесь. Пока только бегло пролистав документацию, выяснил что этот компилятор тоже пригоден для РК86, т.к даёт код КР580 и может выдавать COM-файл (а не INT запускаемый отдельным стартёром). На практике не проверял.  Так как пока меня вполне устраивает Паскаль МТ+, хотя документация (видимо, от другой версии и) местами не совпадает. Чуть позднее, изучив документацию к этому Паскалю, попробую написать Hello World на этом Паскале.

Также недавно нашёл примеры программ на Паскале. Есть несколько десятков исходников инструментальных программ для разных Паскалей, а также есть ещё ~300 исходников программ написанных именно для Паскаля МТ+. Это важно, т.к все Паскали для CP/M имеют различия и несовместимы по мелочам. Речь о исходниках от существовавшей в 80-тые годы BBS MT+ Users Group, от которой сохранился архив содержащий ~300 исходников программ написанных любителями на Паскале МТ+. По исходникам видно, что именно на Паскале было написано несколько десятков полезных инструментальных программ CP/M. Таким образом в качестве примера для целей изучения имеются сотни исходников Паскаль-программ (столько нет даже для CP/M Си, для них есть всего несколько десятков примеров).

А если Вы предпочитаете программировать для РК86 на Си, Коболе, Комале или других, то нет проблем, скачивайте здесь и начинайте писать.

Рассматривая в предыдущих постах CP/M-компиляторы, что пригоды для генерации кода КР580, я не упомянул один ЯВУ, который имеет шанс оказаться лучшим, чем Паскаль, а именно Ада, разработанный в начале 80-тых на базе Паскаля (в этом участвовал тот же автор Н.Вирт). Ясно что переход от Паскаля к Аде несложен, языки лексически похожи. Для нас удачно, что, например, в CP/M Суперсофт-Аду встроен ассемблер и код она генерит для КР580. Я уже подумывал ей заняться. Но теперь, когда нет препятствий к использованию ассемблера с Паскалем, в Аде пока нет нужды (глупо метаться между ЯВУ). Позже будет интересно сравнить качество компиляторов Ады и Паскаля, т.к компилятор Ады написан на 3-5 лет позже, возможно он эффективнее.

Кстати, в CP/M кроме бейсика, Си и Паскаля есть ещё несколько компиляторов старых и даже более новых ЯВУ, являющихся производными от Паскаля. А вот от Си ничего производного нет, т.к С++ возник позже, да и похоже объёма ОЗУ 8-ми разрядки для ООП просто не хватило.

В качестве старых ЯВУ для которых есть CP/M-компиляторы можно упомянуть PL/M, PL/1, Алгол и Фортран (не считая много других экзотических ЯВУ). Два последних, это математические языки, они не годятся для работы по железу. PL/M очень близок к ассемблеру и потому как ЯВУ он не очень мощный (хотя до появления КР580-компиляторов Паскаля в 1978, был популярен). Увы, для PL/M есть единственная книга на русском и её не скачать, можно только купить. Совсем древний PL/1 из 1964 немного похож и на PL/M и на Паскаль и скорее всего вполне подойдёт для разработки ПО для РК86. Важно, что литературы по PL/1 больше, чем для PL/M и его компилятор для КР580 кажется лучше, т.к более свежий, чем компилятор PL/M. Как упомянуто ранее, есть несколько CP/M-компиляторов Ада для КР580. Есть и современные кросс-компиляторы языков Ада, Модула-2 и Оберон (которые Паскаль-производные), но для РК они, увы, не годятся, т.к выдают код для Z80.

И всё же из этих ЯВУ для 8-ми разрядок наиболее удобным для программирования для ретро-ЭВМ любителем ПОКА следует считать Паскаль МТ+ и два Си, BDS и Aztec. Может они и не самые эффективные, но по ним достаточно литературы и документации и большое число примеров, конкретно для данных компиляторов.


Последний раз редактировалось: barsik (Чт Ноя 08 2018, 06:20), всего редактировалось 1 раз(а)
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  QUATTRO в Чт Июн 21 2018, 21:06

Перечитал все статьи, что есть в теме. Как обычно, Барсик написал очень интересно. Но остались вопросы: а как вообще писать программы на Паскале?
Я не спрашиваю, про то, как писать на самом языке Паскаль, а вопрос в том - какие действия нужно произвести, что бы получить готовый код для 86рк или готовые файлы GAM, RK ?
То есть, у меня есть некая папка, в которой имеется 22NICE.COM, что еще нужно положить в эту папку, что бы начать писать на МТ-Паскале?
Ну хорошо, саму программу Hello World (текст программы), я напишу в каком либо блокноте (даже если нужно в DOS-кодировке). Какие должны быть дальнейшие действия, что бы запустился компилятор Паскаля, в эмуляторе 22NICE?

В DOS, и его ТурбоПаскалем, где есть IDE - все ясно. Запустил Паскаль, в синем окне написал что нужно, и получил готовый EXE или COM файл. Но что делать тут - я вообще никакой информации не нашел.
Точнее нашел, как и что делать с Паскалем в ЧИСТОЙ СР/М, не в эмуляторе.

Я извиняюсь, что так криво все это описал, потому что я не знаю как по другому это все описать.
Если эта тема для "бородатых" программистов, и таким как я тут места нет, то извините, что влез в калашный ряд со свои свиным рылом Sad
avatar
QUATTRO
новичёк

Сообщения : 45
Дата регистрации : 2017-08-22
Возраст : 44
Откуда : Москва

Посмотреть профиль http://cnc-controller.ru/

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  QUATTRO в Пт Июн 22 2018, 00:52

Viktor2312 пишет:
Извините, что я влез, на этом форуме, есть место всем, разногласия решаются просто, разные темы, одни не согласные идут в одну тему, другие в другую, и между собой не пересекаются....
Бородатыми - я назвал очень опытных программистов, кому вообще не интересно общаться с начинающими не их уровня (я как раз к таким отношусь, к начинающим не их уровня). Если я кого то обидел, то приношу свои извинения! Я не хотел ни какого скандала или ссоры. А спросил именно тут, потому что на ZX меня бы точно отшили с таким вопросом. Вот там как раз и сидят "Бородатые программисты".
Ну а то, что я назвал себя "свиньёй, которая влезла в калашный ряд" - так это я себя назвал. Тут уже самокритика  Very Happy
Так оно и есть: хочу разобраться в этом, в том же Паскале, но вот что то не получается... Наверное не туда лезу...
avatar
QUATTRO
новичёк

Сообщения : 45
Дата регистрации : 2017-08-22
Возраст : 44
Откуда : Москва

Посмотреть профиль http://cnc-controller.ru/

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  Viktor2312 в Сб Июн 23 2018, 12:09

QUATTRO пишет:Если я кого то обидел, то приношу свои извинения! Я не хотел ни какого скандала или ссоры.

Не поняли вы о чём я, и никого вы не обидели и ссоры никакой нет, ну да ладно. По поводу ваших вопросов, это только наверно Барсик сможет, что-то дельное подсказать, ну или ещё кто-то заглянувший на огонёк.
avatar
Viktor2312
Гуру+

Сообщения : 11045
Дата регистрации : 2012-08-10
Возраст : 39
Откуда : Пятигорск

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  barsik в Сб Июн 23 2018, 21:44

QUATTRO пишет:Если эта тема для "бородатых" программистов, и таким как я тут места нет, то извините
Нет тут бородатых программистов. Бородатые профессиональные программисты с окладом в $6000 в месяц сюда не ходят, у них на это нет времени (они и бородатые потому, что не хотят тратить время на бритьё).

А пишущие здесь все дилетанты или начинающие любители, а читают подобные форумы поначалу вообще полные чайники, с трудом вникающие в смысл обсуждаемого. У меня нет бороды, потому я лишь начинающий в программировании на ЯВУ. У меня не хватило ума и энергии для освоения программирования на современных ЯВУ, это не только очень сложно, но и глупо и неинтересно писать программы для среды, где и так всё есть, причём написано профессионалами. Программирование в стиле рэтро тем и выгодно, что на порядки проще в освоении и тут есть платформы не заваленные программами. Да и вообще тут не менее важен процесс, чем результат.

Компиляция рэтро компилятором делается точно по инструкции пользователя, но не прямо в среде Windows или MSDOS, нужна среда CP/M. Это можно делать в любом эмуляторе, где есть CP/M. Причём желательно таком, где проще переносить файлы из Windows в CP/M. А если компилятор корректный, то можно запускать соответствующий CP/M компилятор и линковщик прямо в MSDOS, для чего нужно конвертировать эти CP/M программы в формат запускаемый в MSDOS c помощью GENCOM пакета 22NICE или подобных. Не все CP/M-программы работают под TSR-эмулятором, но компиляторы обычно MSDOS-корректные и запускаются в MSDOS.

Но я, оттого, что мне так удобнее сразу тестировать скомпилированную программу, использую свой эмулятор ОРИОНА. Для разработки простых программ для РК86 годится любой эмулятор любой 8-ми разрядки у которой есть входы F800 (т.е эмулятор РК, ОРИОНА или Специалиста с доработкой 2-мя диодами, чтоб возникли входы F800). Нужно, чтобы в таком эмуляторе была CP/M с высоким TPA (не менее 48 кб), иначе CP/M компиляторы ЯВУ не работают. А этим двум требованиям, увы, отвечает только эмулятор ОРИОНА. Т.к из-за мизерности объёма ОЗУ, CP/M для РК и Специалиста имеют очень низкий TPA, отчего в них можно использовать только компилятор ассемблера, а компиляторы ЯВУ и вообще все фирменные CP/M пакеты не идут.

Отредактировав текст программы на Паскале, запускаю BAT-файл. Он копирует все файлы с текстом Паскаль программы в каталог FILES.CPM и стартует эмулятор ОРИОНА. В каталог FILES.CPM ещё раньше скопированы все файлы MT+ нужные для компиляции и отладки. При старте эмулятор вылетает в CCP ORDOS. Там в квазидиске B (загруженный из подкаталога ORDOS.B), есть файл с именем EXT$.

Этот файл EXT$ автостартует при входе в ORDOS, что в некотором роде заменяет AUTOEXEC. В файл EXT$ переименован код CP/M оформленный как запускаемый ORDOS-файл (у меня все версии DOS в виде ORDOS-файлов, т.к это на порядок удобнее, чем старт DOS с системных треков дискеты, т.к позволяет выбирать  версию, а не иметь только одну и избавляет от подкачки). Можно и не использовать автостартовость ORDOS, а вручную вводить в CCP ORDOS командную строку с именем запускаемого файла DOS. Обычно это D1<ВК> (ORDOS файлы D0$ и D1$ это соответственно CP/M ОРИОНА для банок 0 и 1).

В моём эмуляторе после старта CP/M все файлы из подкаталога FILES.CPM перегружаются в эл.диск из ОЗУ ОРИОНА. Физически эл.диск это ОЗУ PC, потому размер эл.диска в моём эмуляторе невелик, ~280 кб (эмулятор использует только ~600 кб конвенционального ОЗУ MSDOS), но этого хватает (например, объём компилятора MT+ лишь ~150 кб). Т.е в папку FILES.CPM помещаю файл с именем AUTOEXEC.SUB следующего содержания.

Код:
D7
MTPLUS P $PAX
MTPLUS MOD1 $PAX

Первая строка это запуск консольного драйвера CP/M, т.к без него в CP/M ОРИОНА только КОИ-7, а все фирменные программы используют ASCII. Вторая строка компилирует основную программу с именем P.PAS (которуя пишу и отлаживаю в данный момент). А третья строка компилирует модуль связи MOD1.PAS тоже на Паскале, обеспечивающий интерфейс с подпрограммами ПЗУ РК86, который общий для всех программ, потому и вынесен в отдельный модуль.

Этот модуль связи написанный на Паскале может заменяться эквивалентным модулем написанным на ассемблере, который содержит процедуры для интерфейса между Паскалем и п/п-ми ПЗУ РК86, а также процедуры работающие прямо по железу РК, что Паскаль, естественно, не умеет. Это приходится применять для сложных процедур и функций. Этого можно было бы избежать, ограничившись INLINE ассемблерными вставками, если бы встроенный INLINE ассемблер позволял переходы. Но увы, несмотря на то, что в документации написано, что символ '*' адресует текущий адрес трансляции, это не работает. Без переходов оператор INLINE позволяет встроить в программу только линейный фрагмент на ассемблере. Для простого вызова п/п-мм ПЗУ этого достаточно. Хотя выгоднее написать на ассемблере и включить в программу свои аналоги п/п-мм ПЗУ, что сделает программу более мобильной. В случае внешнего модуля на ассемблере вместо 3-й строки ставится строка: M80 =MOD1.ASM, которая компилирует с ассемблера.

Обычно при трансляции возникает куча ошибок трансляции и приходится их исправлять. Тогда нажимаю F9 и вылетаю в монитор-отладчик эмулятора. Здесь набираю Q<Enter> и вываливаюсь из эмулятора назад в Windows. Где в окне редактора исправляю текст и повторяю компиляцию снова запустив эмулятор.

Если всё странслировалось без ошибок, то в ком.строке CP/M пишу S L<ВК>, отчего SUBMIT запускает пакетный SUB-файл L.SUB (SUB-файлы это CP/M аналоги BAT-файлов MSDOS). Это просто, чтобы меньше набирать текста. Можно просто ввести командную строку для запуска линковщика:

LINKMT P, MOD1, PASLIB /S

Иногда, например, чтобы получить и посмотреть ассемблерный текст в который компилируются операторы Паскаля или, чтобы подключить отладчик, добавляются другие ключи. При компоновке тоже постоянно возникают ошибки линковки, приходится их устранять и всё повторять. Когда программа наконец скомпилирована и слинкована, то на эл.диске возникает файл P.COM. Я загружаю его в ОЗУ на адрес 100 командой ZS3 P.COM<ВК> и делаю сброс ОРИОНА нажав на F8.

Снова оказываюсь в ORDOS. Только теперь автостарта EXT$ не происходит, т.к квазидиск B затёрт при старте CP/M в банке 1 (а сама копия CP/M для её рестарта сохраняется в банке 6 ORDOS файлом с именем CPM$). Теперь мне надо перенести мою программу, что осталась в банке 1 (где работает CP/M)  в банку 0, где программе надо работать (т.к в банке 1 она работать не может потому что ПЗУ F800 можно вызывать только из банки 0, т.к экран в банке 0). Копировать из банки в банку на ОРИОНЕ умеет только монитор M3-EXT.

Для этого ввожу команду MON<ВК>, отчего из ROM-диска стартует M3-EXT. С его помощью командой P0,C3FF,1,0 копирую память банки 1 в банку 0. При этом и программа, что была загружена на B1:0100 отладчиком ZSID3, переносится в банку 0 на адрес 100. Остаётся для её запуска ввести команду G100<ВК> и можно смотреть как работает программа написанная на Паскале.

Так можно проверить на ОРИОНЕ только корректные программы РК86, что делают ввод/вывод только подпрограмами ПЗУ. Если же я делаю программу, что напрямую лезет в экранный буфер РК86, сама напрямую опрашивает матрицу клавиш или использует альтернативный фонт, то проверить такую программу на ОРИОНЕ нельзя.

Но по счастью у меня как раз есть эмулятор РК86 на ОРИОНЕ. Чтобы проверить такую программу, я записываю проверяемый файл в квазидиск ORDOS командой SP@,100,endaddr<ВК> и запускаю эмулятор РК на ОРИОНЕ командой EM<ВК>.

Быстрее будет компиляция файлами MSDOS прямо в Windows. Это точно будет быстрее, т.к при компиляции в эмуляторе ОРИОНА у меня каждый раз теряется куча времени на запуск самого эмулятора.

Для этого с помощью GENCOM из пакета 22NICE делаем MSDOS стартовые файлы из файла компилятора и линковщика MTPLUS.COM и LINKMT.COM (для чего сначала переименовываем их в *.CPM и запуская GENCOM получаем их MSDOS стартёры). Что позволяет их стартовать BAT-файлом прямо в Windows. Но получив готовую скомпилированную программу, её придётся вручную сконвертировать в GAM или RKR файл и запустив какой-либо эмулятор РК86 считать в эмулятор этот Tape-файл. После чего для проверки можно программу запустить командой G100<ВК>.


Последний раз редактировалось: barsik (Чт Ноя 01 2018, 01:24), всего редактировалось 1 раз(а)

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

.

Сообщение  barsik в Пн Июл 09 2018, 14:17

Пока искал информацию про Паскаль МТ+ вот здесь мне попалась интересная статья kakos_nonos-а про компиляторы ЯВУ. Автор озадачился той же проблемой (поиском компиляторов для программирования РК86), хотя его выводы немного отличаются. Автор также пришёл к выводу, что наиболее победительный язык высокого уровня для программирования РК86 это PL/M (точнее не оригинал PL/M из 1973, а его более продвинутый вариант PLMX из 1979).

Однако выводы автора относительно неактуальности Паскаля МТ+ в корне ошибочны. Автор не разобрался в ключах трансляции и подключал ненужные в конкретной программе библиотеки (например, зачем подключать библиотеку с плавающей точкой, если все расчёты идут над целыми числами). Считать с точностью 38 цифр после запятой в программах для РК не требуется, а банковских программ для РК никто писать не собирается. Потому автор получал для простейшей программы объём кода в 26 кб из чего и сделал вывод о непригодности Паскаля МТ+. Я странслировал ту же программу и получил объём кода менее 4 кб (причем объём собственно программы едва 300 байтов, остальные 3.5 кб это RUN-тайм библиотека, которую не проблема сократить в объёме в разы). Я получал объём минимальной программы на МТ+ менее килобайта, кстати, на Си размер минимальной программы в разы выше.

Нашёл ещё один Паскаль, который возможно может быть использован для КР580. Это Hisoft Pascal80. Вообще-то он для Z80. Он нормально компилирует в CP/M ОРИОНА на Z80, но выходной код компилятора в моих пробных экспериментах почему-то работал и на КР580.

Этот Паскаль был написан для ZX-Spectrum в 1983. В 1984 вышла версия для Amstrad CPC и версии для CP/M 2.2 и 3.0 (версии для CPC поддерживают железо CPC, графику и т.п). В 1985 вышла версия для MSX, а позднее и для других машин с Z80. Интерес представляет версия для CP/M 2.2.

Читал, что по эффективности этот Паскаль считается более эффективным (код короче и быстрее), даже чем Турбо-Паскаль. Простые опытные программы, что я компилировал давали вполне компактный код (менее 4 кб). Удачно, что, т.к этот компилятор широко использовался, документации вполне хватает. Кстати, в именах в программах для HP80 не используйте символ "подчёркивание" (код $5F) и у меня почему-то транслировалось только, если все операторы были в верхнем регистре (возможно нужно задать какой-то упр.ключ).

Сам компилятор работает только на Z80 (в эмуляторах CP/M для КР580 не работает). Пока не могу точно проверить работает ли весь генерируемый код в системе с КР580, т.к, хотя у меня есть эмулятор ОРИОНА на КР580, но нет CP/M для ОРИОНА, что работает на КР580. Это проверить можно в эмуляторе EMU от b2m, но там неудобно переносить файлы в образы диска, потому для прогона CP/M-программ я им не пользуюсь.

Потому тестирование кода из под компилятора проводил на IBM PC под MSDOS TSR-эмулятором 22NICE. Этот эмулятор автоматически переключается на Z80, если в программе встречается хоть одна команда Z80 (если же Z80-команд нет, то флаги ставятся точно, как у КР580). Если сгенерировать COM-файл с ключом "PROCESSOR=TEST", то выдаётся сообщение использовала программа команды Z80 или нет. Так вот, несколько простых Паскаль программ я странслировал с помощью компилятора HP80 и прогнав их под 22NICE, выяснил, что команд Z80 в их коде нет.

Естественно, по простейшим программам ещё нельзя судить. Нужно испытать на больших программах в которых задействованы все операторы этого Паскаля. К сожалению, это специфичный Паскаль (например, в нём нет оператора STRING) и проверить его на программах написанных для TP или MT+ не получается. Нужно изучать документацию, чтобы понять как писать программы для такого компилятора. Но пока меня устаивает и Паскаль МТ+.

Hisoft Pascal80 можно скачать здесь. Сам компилятор (не дистрибутив, лишь сам компилятор), который я проверял - это файл HP80.COM  в подкаталоге HP80. Полный дистрибутив в файлах образов дисков CPM Sam Coupe и DSK-образах для CPC (для считывания файлов из образов дискет CPC используйте там же приложенную утилиту чтения образов дискет CPC).

Наконец также удалось найти компиляторы ЯВУ для процессора 6800. Если для процессора 6502 можно скачать множество кросс-компиляторов ассеблера и ЯВУ (есть даже кросс-IDE), то для процессора 6800 с этим намного сложнее. Если кое-какие ассемблеры для 6800 имеются (есть даже кросс-ассемблер для CP/M, хотя и не макро), т.к ассемблер был необходим при разработке программ контроллеров на 68xx, то компиляторы ЯВУ (из-за того, что бытовых ЭВМ на 6800 практически не было), - распространены намного меньше.

Но всё-же и эта проблема в итоге решилась. В качестве компилятора Си нашёлся многоплатформенный кросс-компилятор Си от Hi-Tech. А в качестве  компилятора Паскаля удалось найти компилятор Паскаля для болгарского компьютера "Пылдин-601". Есть версия Паскаля и для оригинальной DOS этой машины и, главное, есть кросс-компилятор для PC. Кстати и компилятор ассемблера для Пылдина, это, похоже, лучший компилятор ассемблера для 6800 (и он также есть в кросс-версии для PC). Нашёл также компилятор Паскаля для процессора 6502.


Последний раз редактировалось: barsik (Пн Ноя 12 2018, 15:03), всего редактировалось 1 раз(а)

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  QUATTRO в Вс Авг 26 2018, 17:37

barsik пишет:
Нет тут бородатых программистов. Бородатые профессиональные программисты с окладом в $6000 в месяц сюда не ходят...


К сожалению я не получил оповещения об ответе и сюда заглянул просто так, может кто напишет ответ на мой вопрос. Как я и ожидал, ответил только Барсик, за что ему ОГРОМНЕЙШЕЕ СПАСИБО!!! Как всегда развернутый ответ и приятно было почитать! Теперь надо пробовать по написанному Барсиком.
Но и все же, сколько я не пробовал написать что то простейшее на Паскале, вместе с 22NICE - у меня ничего не вышло. Я так и не понял как привязывать программу к стандартным подпрограммам 86рк. Ведь действительно, как barsik уже писал, ничего не получится без привязки к стандартным подпрограммам Монитора. А если писать заново свои подпрограммы, то получается масло-масляное, да и лишнюю память использовать.

Тут же не как в обычном MS-DOS - получили .COM файл и запустили его (это я про Радио-86рк).
Как я понимаю, в конечном итоге я должен получить какой то код по адресам, например 0000 - 00FF, потом это все как то перенести на тот же 86РК и уже по адресу G0000, запустить. В эмуляторе это все сделать проще (запустить), но и все же, надо сначала получить результат, тот же файл HEX-код или хотя бы файл с расширением GAM/RK и уже его запускать в эмуляторе.

*****
Извиняюсь за флуд, и сразу хочу сказать, что я никого не хочу обидеть, но для меня такие как barsik, Морозов (Vinxru), kakos_nonos и подобные - это те самые "бородатые" программисты, которые вот так вот возьмут и за вечер напишут какую нибудь программу, пусть даже маленькую (или большую), кто на Паскале, кто на Асм, кто на Си, не важно. Или посмотрев на код, сразу найдут ошибку и т.п.
Я когда то переписал вручную Монитор для Микролаб, в обычном блокноте, выставил его напоказ, перед этим несколько раз проверил, но мне нут же написали, что в коде есть ошибки Smile Вот это я понимаю люди разбираются!
Я бы был счастлив, если бы я был таким и меня считали и называли - бородатый программист Smile Хотя у меня и никогда не было и не предвидеться бороды Smile
Я до сих пор (иногда сажусь) и ищу ошибку в Микролаб и так ничего и не нашел, ни в схеме ни в Мониторе.
*****
Еще раз извиняюсь за флуд.
Кстати, оклады в 6000$ это скорее всего в Европе/Америке. В России таких окладов нет, даже у "бородатых" программистов. А если и есть, то это у единиц. Основная армия получает 30-150 тыс. Единичные доходят до 250-300 т., да и то это уже скорее начальники отделов, а не рядовые программисты.
А не заходят они сюда, потому что мало кто разбирается в тех же старых компьютерах и программировании их. Все больше на чем то современном пишут.
avatar
QUATTRO
новичёк

Сообщения : 45
Дата регистрации : 2017-08-22
Возраст : 44
Откуда : Москва

Посмотреть профиль http://cnc-controller.ru/

Вернуться к началу Перейти вниз

.

Сообщение  barsik в Ср Окт 31 2018, 23:10

san010101, я очень давно ответил в личке на Ваш E-mail посланный из этого форума по адресу E-mail из моего профиля. Так как при этом обратным адресом указывается не Ваш личный почтовый адрес, а данный форум, и т.к в Вашем профиле Вы не указали свой почтовый адрес, не мог ответить E-mail-ом, только личкой. По содержимому папки "Исходящие" видно, что письмо отосланное по ЛС, Вами так и не прочитано.

Не писал на РУ-ЭВМ последнее время по несколькимм причинам. Во-первых, особых успехов по программированию на Паскале нет, сам ещё учусь. К тому же, занимался за это время данной темой мало. Во-вторых, некоторое время и не мог писать в форум по техническим причинам.

Должен пояснить, что в программировании я сейчас вообще почти полный чайник, хотя в 90-тые и пытался освоить программирование, в том числе для PC. Немного программировал в MSDOS на ассемблере, Турбо-бейсике, Турбо-Паскале и Турбо Си для MSDOS. А когда в начале 21-века до меня наконец дошло, что угнаться за прогрессом невозможно (пока без всякой помощи или хотя-бы консультаций самостоятельно несколько лет осваиваешь новый инструментарий, он успевает уже устареть и надо начинать всё сначала), то понял, что просто впустую трачу время пытаясь освоить Visual Basic, Visual Си и Delphi. А сейчас почти 20 лет спустя и те знания по программированию на ЯВУ, что поимел, полностью забылись. А вот, кстати, ассемблер, слишком прост и нагляден (особенно ассемблер Z80), его не забудешь, даже при желании.

Наиболее эффективная разработка, практически делающая ненужной использование отладчиков, это транслировать после каждого небольшого изменения исходника. Тогда, если что-то пошло не так, сразу ясно где ошибка и не требуется долго прогонять программу под отладчиком, трассируя покомандно, долгими часами тщетно пытаясь понять в каком месте ошибка.

Отладчик при этом не нужен, - когда возникает неясность, в программу вставляются отладочные фрагменты, т.е останов в контрольных точках с печатью содержимого регистров и ячеек, имитируется как бы прогон под отладчиком. Метод частых перетрансляций намного эффективнее, чем древний (вынужденный из-за медленности техники) метод, когда программист несколько часов набирает исходник программы, а затем начинает долгими часами прогонять её под отладчиком, пытаясь обнаружить ошибки. Это делалось потому, что каждая перетрансляция длится очень долго и сильно изнашивает дисковод.

Сначала я пытался использовать компилятор Паскаля МТ+ в эмуляторе 8-ми разрядки, в котором эмулируется дисковод с CP/M. Но оказалось, что исходники реального размера (тысяча и более строк) на скорости 8-ми разрядки транслируются слишком долго, - до 10 минут. Удаётся ускорить потери времени на трансляцию до одной-двух минут, если не перетранслировать всякий раз всю программу, а лишь те небольшие модули в которых последний раз делались изменения. Но всё-равно, вместо 20 перетрансляций в час кросс-средствами, в эмуляторе за то же время удаётся успеть сделать всего 3-5 перетрансляций.

Чувствительным неудобством компилятора Паскаля оказалось то, что он останавливает трансляцию по первой же ошибке. Ассемблер транслирует всю программу и выдаёт сразу все ошибки. Тогда видя их в листинге трансляции, их все сразу можно исправить за одну итерацию. А на Паскале, если в исходнике есть 50 ошибок, то для каждой ошибки надо выполнять трансляцию, чтобы компилятор указал в какой строке и какой номер ошибки возник.

Всё это привело к тому, что эффективность Паскаля с использованием компилятора в эмуляторе или реале оказалась не лучшей, чем при программировании на ассемблере. Выигрыш за счёт использования ЯВУ обнулил медленный компилятор. Хотя, конечно, ЯВУ облегчает разработку программ со сложной логикой, многое, что крайне сложно сделать на ассемблере, без всякого напряжения делается на ЯВУ. Программы на ЯВУ намного понятнее и их на порядок легче сопровождать и модифицировать.

Т.о ясно, что трансляция в эмуляторе 8-ми разрядки это плохой вариант. Чтобы повысить эффективность требуется до нуля сократить потери времени на саму трансляцию. Это достигается при использовании кросс-компилятора Паскаля. Но не смог найти кросс-компилятор Паскаля в коды КР580/Z80. А вот кросс-компиляторов Си нашёл с десяток. Можно использовать конвертор исходников на Паскале в исходник на Си. Тогда текст из под конвертора можно транслировать кросс-компилятором Си. Но увы, пока не нашёл кросс-компиляторов Си под КР580, все под Z80. Похоже, что вне бывшей СССР КР580 малопопулярен. Использование Паскаль+Си невыгодно тем что лишний этап на конверсию чреват дополнительными ошибками.

Для ускорения трансляции, если используется Win XP, кроме кросс-компиляторов, можно прогонять CP/M-компилятор Паскаля под CP/M TSR-эмулятором для MSDOS. Для этого следует использовать версию МТ+ от Digital Research, а не раннюю русифицированную версию от СМ-1800. Грамотно составив BAT-файл и используя вспомогательную конвертирующую программу, сейчас мне удаётся транслировать Паскаль исходник в ORD-файл всего за несколько секунд. Но такой ORD-формат файлов использует только мой эмулятор. Если используются эмуляторы EMU или EMU80, то для них нужно писать специальный конвертор, который конвертирует блок кодов в TAPE-формат принятый для этих эмуляторов или же вручную как-то переносить тестируемую программу в образ дискеты.

QUATTRO пишет:Я так и не понял, как привязывать программу к стандартным подпрограммам РК86. Ведь действительно, как barsik уже писал, ничего не получится без привязки к стандартным подпрограммам Монитора.

Проблема в том, что в Паскале нет оператора CALL/USR как в бейсике. Зато есть способ это сделать за счёт встроенного инлайн-ассемблера. Потому и приходится написать процедуры или функции, которые не делают ничего кроме вызова п/п-рамм ПЗУ.

Насчёт того, что я "писал, что ничего не получится без привязки к стандартным подпрограммам ПЗУ". Вряд ли я писал именно так. Скорее всего имелось ввиду, что использовать имеющиеся готовые подпрограммы проще всего. Если при программировании на ассемблере стандартные п/п-ммы ПЗУ существенно упрощают программирование, то и из ЯВУ это тем более имеет смысл делать.

Фраза, что на ЯВУ без использования подпрограм ПЗУ совсем ничего не получится, - неверна. Ведь полно РК-игр, что не лезут в ПЗУ, напрямую считывая матрицу клавиш через ППА и записывая байты напрямую в экранный буфер. Для машины с текстовым экраном программирование на ЯВУ без использования подпрограмм ПЗУ конечно получается в разы тормознее, хотя, благодаря мощности ЯВУ, совсем не сложно. А вот для графических ЭВМ это сделать сложнее, и к тому же в результате получится совсем тормоз.

Чтобы не вызывать ПЗУ, можно написать на самом Паскале аналоги ПЗУ-шных подпрограмм вывода на экран и опроса клавиатуры. Ясно, что это тормознее, но не сложно. На Паскале написание подобных процедур проще, чем на ассемблере. Например, вывод символа, это просто по текущим координатам POSX и POSY вычисление экранного адреса по формуле и занесение туда байта. На Паскале это десяток строчек (и даже меньше, если ролик сделать ассемблером), тогда как на ассемблере это 300 строк. Так же и опрос клавиатуры - делаем на Паскале то же, что делает подпрограмма F81B, - выкидываем в порт А "бегущий ноль", считывая с порта B, до тех пор пока не будет нуля в одном из разрядов. А опрос клавиш не входящих в матрицу - УС, СС, РУСЛАТ это вообще один хитрый оператор Паскаля (считать ячейку с маской). То, что на ассемблере пишется за многие часы или дни, на ЯВУ пишется за единицы минут.

Если скоростей Паскаля не хватает, можно сделать то же самое на ассемблере, т.е взять из исходника ПЗУ РК86 несколько основных подпрограмм, странслировать каждую отдельную подпрограмму как процедуру и в виде инлайн-дампа встроить в Паскаль программу. Тогда будут быстрые Паскаль-процедуры выполняющие ввод/вывод на РК86 без доступа к его ПЗУ.

Но всё-же проще использовать Паскаль-процедуры, реализованные с использованием инлайн-ассемблера, которые ничего не делают кроме переадресации на подпрограммы ПЗУ. Вызовы ПЗУ заменяются инлайн-вставкой, например, INLINE ( "CALL/$F803" ); что мало отличается от CALL 0F803H в ассемблерной программе. Оформление этого как отдельные процедуры и функции нужно лишь, чтобы не повторять всякий раз один и тот же код.

Как писал выше, в бездисководной и без-CP/M-ной системе стандартные паскалевские операторы ввода/вывода (READ, WRITE) не работают. Использование собственных процедур ввода/вывода быстрее и удобнее, но начинающему изучение Паскаля всё-же желательно, чтобы все операторы работали как в учебнике, чтобы ввод/вывод, по крайней мере на консоль, делался стандартными операторами READ и WRITE.

Паскаль программа это делает вызывая функцию @BDOS библиотеки времени исполнения, которая, естественно, просто загружает параметры в регистры BC,DE и делает CALL 5. Документация рекомендует для не-CP/M систем переписать функцию @BDOS. Но исходника RUN-тайм библиотеки нет и непонятно как это сделать. Переопределение функции @BDOS не помогает, выдаётся ошибка двойного определения идентификатора.

Но недавно я кое-как решил эту проблему. Проще всего результат достигается переопределением процедур @WNC и @RNC. При их переопределении ошибки не возникает. После вставки в текст программы нижеприведённого куска, оператор WRITE для консоли начинает работать, он работает используя не функцию CP/M, а подпрограмму РК86. Но работает только для консоли (не для DOS-файлов, только для стандартного устройства OUTPUT, который в Паскале тоже является файлом).

Код:
procedure @WNC(SYM: byte);
begin
   inline( "LDA/SYM /
           "MOV C,A/
           "CALL/$F809 );
end;

function @RNC: char;
var result: char;
begin
   inline( "CALL/$F803/
          "STA /result );
   @RNC := result;
end;

А вот для оператора READ всё сложнее, после вышеприведённого переопределения @RNC при READ программа всё-равно виснет в без-CP/M-ной системе. Потому что ввод/вывод в Паскале не прямой, а буферизованный (для консоли он происходит из стандартных файлов INPUT/OUTPUT). Потому, чтобы заработал READ надо переписать и функцию GET, что уже не так просто, т.к она работает с файлом. Оператор READ в Паскале, это не просто ввод строки, а ввод всего. Он позволяет ввод любых переменных, строк, символов, простых чисел (как десятичных, так и HEX), REAL-чисел (т.е дробных десятичных чисел) и даже любых данных любого размера (называемых записями) из открытых файлов.

Дробные десятичные числа с точностью в 38 цифр после запятой необходимы для расчёта полёта на Луну или банковских программ, но для написания РК-игр это не надо. Т.е полноценный READ не нужен, достаточно трёх подпрограмм: ввода строки (GETLIN) и ввода из полученной строки десятичного или HEX-числа. Тем не менее полезно переопределять $RNC на пустой оператор, т.к это существенно сокращается объём кода программы. Если же всё-же Вам надо вводить дробные десятичные числа, то чтобы заработал оператор READ, придётся эмулировать работу CP/M. Эта задача решается перехватом обращений к CP/M BDOS по CALL 5.

Для этого в начале программы выполняется вот такой фрагмент: poke(5,$C3); dpoke(6,addr(CPM)); В программе должна быть функция CPM, которая должна эмулировать все недисковые функции CP/M. Эта функция по сути является эмулятором BDOS CP/M. Я сделал её как на Паскале, так и на ассемблере (смотри спойлер ниже). Но это громоздко, использовать имеет смысл только, если в программе необходим ввод дробных чисел. А для вывода по WRITE вариант с @WNC намного проще и экономнее. К тому же своя процедура ввода REAL-числа может оказаться короче, чем объём кода эмулятора BDOS.

И кстати, подобный эмулятор консольных функций CP/M (на адрес 0...FF) опубликовали авторы РК86 ещё в 80-тые годы, он предназначался как раз для использования на РК недисковых CP/M-программ и чтобы программы РК написанные с использованием CALL 5, в будущем, когда на РК появится CP/M (на базе RAM-диска или даже с настоящим дисководом) программы не пришлось бы адаптировать для неё.

Использование в инлайн-вставках меток для команд переходов также кое-как извращённо удалось решить. Для этого в исходнике на ассемблере полученном после отработки DIS8080 приходится смотреть и записывать на бумажку смещения меток от начала процедуры. А в начале прогона программы оператором ADDR получаем адрес конкретной ассемблерной инлайн-вставки и корректируем байты где нужны абсолютные адреса, добавляя к значению адрес начала процедуры.

Есть ещё один способ, когда инлайн-ассемблерная процедура копируется на другой заранее известный адрес, отчего адреса меток становятся известными и код не нуждается в коррекции, по сути это метод широко применяемый для вставки ассемблерного кода в бейсике.

Вообще, для экономии ОЗУ и удобства использования на РК программ созданных для CP/M, в том числе и написанных на Паскале, можно в свободный участок ПЗУ, например, в адреса F000...F7FF прошить эмулятор консольных функций CP/M (естественно перетранслировав на соответствующие адреса). Нижеприведённый простейший эмулятор консольных функций BDOS имеет размер всего в 256 байт и позволяет в оригинале использовать все недисковые (и нелезущие в CP/M-BIOS) программы CP/M. Перед стартом CP/M-программ, написанных самостоятельно на ЯВУ или взятых из архивных сайтов CP/M достаточно выполнить инициализацию (при которой формируются JMP-ы в адресах 0000, 0005 и JMP на вход в BDOS в вершине ОЗУ).

эмулятор консольных функций BDOS CP/M:

Код:


;  Эмулятор консольных функций 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


QUATTRO пишет:Как я понимаю, в конечном итоге я должен получить какой-то код по адресам, например 0000 - 00FF, потом это все как то перенести на тот же РК86 и уже по адресу G0000, запустить. В эмуляторе это все сделать проще, но и все же, надо сначала получить результат, тот же файл HEX-код или хотя бы файл с расширением GAM/RK и уже его запускать в эмуляторе.

Естественно, для прогона результирующий код из под ЯВУ надо перенести в эмулятор или реал и разместить по рабочим адресам, а затем командой G запустить с стартового адреса. Но вот насчёт адреса 0 не всё просто. Без всяких ухищрений все CP/M компиляторы на выходе выдают программы стартующие с адреса 100H. Тут уж ничего не поделать, это свойство CP/M. Но это даже имеет свои плюсы. Вы без хлопот получаете 256 байт под разные буфера или свой внешний программный код.

Например, во все DOS я всегда вставлял блок RST. Это блок входов заимствованный из одного заграничного компьютера. RST команды короткие и удобные. Например после загрузки RST-блока текст удобно выдаётся командой RST 18, разместив ASCIIZ-строку сразу же за кодом команды RST, это ускоряет и экономит 5 байтов при каждом выводе и текст располагается там же где его выводят, что облегчает изучение программы.

HEX-формат выдают нелинкующие ассемблеры первой волны из 1976-1978 годов. HEX-формат появился в 60-тые, рассчитан на перфоленту и сейчас является полнейшим анахронизмом. Он разработан для хранения на ненадёжном носителе, а также позволяет при вводе с перфоленты прямо в ходе процесса загрузки определить, что произошёл сбой. Нет никакого смысла использовать такие ассемблеры. Зачем транслировать в HEX, чтобы потом специально приходилось дополнительно отдельной программой конвертировать в код. Вам же не надо выводить файл на перфоленту. Хотя 40 лет спустя этот формат стали применять в прошивателях РПЗУ, т.к он позволяет хранить коды с адресами и фрагментами. HEX-формат для РК86 неприменим, для него нужен код программы.

Получить сразу файл в TAPE-формате эмуляторов (т.е *.GAM или *.RK) на Паскале не получится. А вот на ассемблере это делается без проблем, там просто в начале исходника операторами DB вставляется TAPE-хеадер, а в конце файла также вставляются требуемые байты синхронизации и контрольная сумма. Потому, если нужен TAPE-формат эмуляторов, то конвертировать код программы в такой формат придётся вручную. Но разумнее написать на Паскале простую программку, которая конвертирует в требуемый для конкретного эмулятора формат (это написать просто и будет дополнительная тренировка в Паскале). Можно такой конвертор написать в Паскале для PC или в Паскале для CP/M (тогда для запуска в MSDOS нужен резидентный CP/M-эмулятор типа 22NICE)

Спойлер:
В свой эмулятор РК я в 1999 к поддержке ORD-формата специально добавил загрузку GAM-формата, увидев его в эмуляторе А.Дёмина. А в моих эмуляторах других ЭВМ TAPE-форматы вообще не поддерживаются, т.к они просто низачем не нужны. Нужные файлы сами автоматически оказываются в квази-дисках и эмулируемых дискетах. Универсальный формат ORD удобнее всех надуманных TAPE-форматов, к тому же он универсальный для всех машин.

Формат ORD и получается проще, просто добавлением в начало блока хеадера в 16 байт. А Паскаль как раз ровно 16 байт в начале выходного кода оставляет пустыми, заполненными нулями. Достаточно загрузить файл в UltraEdit (т.к это код он включится в HEX-режиме) и вручную отредактировать эти байты, сформировав ORDOS-метку. А удобнее это тем, что ORD-файл не требуется грузить в эмулятор отдельной командой имитирующей загрузку с МГ. ORD-файл сам загрузится в квазидиск при старте эмулятора и пользователю для тестирования остаётся только его запустить набрав после пробела имя.

Чтобы знать при программировании на ЯВУ что происходит, удобно вставлять в заданных стоп-точках вывод дампа (с ASCII). Это позволяет увидеть как представляются и передаются процедурам данные, увидеть переменные, т.е посмотреть как Паскаль работает изнутри. Хотя, в отличие от BDS и AZtec Си у Паскаля МТ+ есть отладчик, и для этих же целей можно использовать его, но на это теряется намного больше времени. Кстати, при программировании на ассемблере при нужде я также к программе прилинковывал модуль с аналогичной подпрограммой вывода дампа.

Потому при начале изучения Паскаля я сразу же написал процедуру дамп. Она вполне грамотная (в отличие от любительских дампов). Она подгоняет адреса под кратные и выводит байты и символы в позициях соответствующих адресам, даже если входной адрес не кратный. Первый параметр это адрес, вторым параметром передаётся необходимое число строк дампа (чтобы посмотреть, например, строчную переменную длиной менее 10 букв нет смысла загромождать экран выводом дампа на всю страницу).

dump.pas:

Код:
procedure dump(adr: integer; byt: byte);  { byt задаёт число строк дампа}
var cur,i: integer;

procedure symcout(bb: byte);
begin
    if bb<32 then cout('.') else cout(chr(bb)); space;
end;

begin
  cur:= adr & $FFF8;
  while byt >0 do
    begin
      cr; whexbl(cur); space;

      i:=0;
      repeat
        if (cur+i) < adr then begin spaces(3); i:=i+1; end
           else
         begin
           hexbl(peek(cur+i)); i:=i+1;
        end;
      until i=8;

      i:=0;
      repeat
        if (cur+i) < adr then begin spaces(2); i:=i+1; end
        else
          begin
            symcout($7F & peek(cur+i)); i:=i+1;
        end;
      until i=8;

      cur:=cur+8; byt:=byt-1;
    end;
    cr;
end;

А вот аналог этой процедуры на ассемблере:

dump.asm:

Код:
       .Z80
        aseg
        ORG     100H
        
KOI7    EQU     1               ; =0, если драйвер КОИ-8
RABADR  EQU     06000H

MCONIN  EQU     0F803H
MCOUT   EQU     0F809H
WBOOT   EQU     0F86CH

; ----------------------------------------------

INIRST  MACRO   RSTADR,ADDR
        LD      A,0C3H
        LD      (RSTADR),A
        LD      HL,ADDR
        LD      (RSTADR+1),HL
        ENDM
        
; ----------------------------------------------

.djnz   MACRO   ADDR
        DEC     B
        JP      NZ,ADDR
        ENDM

; ----------------------------------------------

EMIT    MACRO   SYM
        LD      A,SYM
        RST     10H
        ENDM

; ----------------------------------------------

ROVNO   MACRO
ZZZ     aset    $ and 0FH
if      ZZZ     ne 0
        rept    10h-ZZZ
        defb    255
        endm
endif
        ENDM

; ----------------------------------------------

        defb    'DUMP@',32,32,32
        DW      RABADR,LENGTH,0000,0000
        
        .phase  RABADR
        
        INIRST  38H,WAIT
        INIRST  10H,SCOUTA
        CALL    TEST
WAIT:   CALL    MCONIN
        JP      WBOOT
        
TEST:   LD      HL,STROKA
XDUMP:
        CALL    SKIPHL
        OR      A
        JP      Z,XDUMP4        ; JMPR
        
        CALL    HDIGIT
XDUMP2:
        LD      A,L
        AND     0FH
        LD      B,A
        LD      A,0C0H          ; длина дампа
        JP      Z,XDUMP3        ; JMPR
        SUB     B
XDUMP3: CALL    DUMPIN
        RST     38H             ; на CCP

; ----------------------------------------------

XDUMP4: defb    21H             ; LD HL,(DMPADR)
DMPADR: DW      100H
        JP      XDUMP2          ; JMPR
        
; ----------------------------------------------

DUMPIN: LD      (RDUMP),A
DUMP:   PUSH    HL
        PUSH    DE
        PUSH    BC
        CALL    CR_HEX
        CALL    SPACE2
        LD      A,L
        AND     7
        JP      Z,DUMP1         ; JMPR
        LD      B,A
        ADD     A,A
        ADD     A,B                  ; *3
        LD      B,A
        CALL    SPACEB
DUMP1:  defb    06H             ; LD B,NN
RDUMP:  defb    0C0H           ; длина дампа в байтах
DUMP0:
        PUSH    BC
        PUSH    HL
DMPLO1:
        LD      A,(HL)
        CALL    HEX_A           ; CALL HEX_BL
        CALL    SPACE
        INC     HL
        LD      A,L
        AND     7
        JP      NZ,DMPLO1       ; JMPR
        CALL    SPACE
        POP     HL

        LD      A,L
        AND     7
        CALL    NZ,ASCII1

        POP     BC
DMPLO2:
        PUSH    BC
        LD      A,(HL)

if      KOI7
        OR      A
        JP      M,DUMP4
endif
        CP      20H
        JP      NC,DUMP5        ; JMPR
DUMP4:  LD      A,'.'
DUMP5:
        RST     10H
        CALL    SPACE
        INC     HL
        POP     BC
        .djnz   DUMP3
        LD      (DMPADR),HL
;       JP      POPREG
POPREG: POP     BC
        POP     DE
        POP     HL
        RET
        
; ----------------------------------------------

ASCII1: ADD     A,A
        LD      B,A
SPACEB: EMIT    20H
        .djnz   SPACEB
        RET
        
; ----------------------------------------------

DUMP3:  LD      A,L
        AND     7
        JP      NZ,DMPLO2       ; JMPR
        CALL    NEWLIN          ; это: CR, HEX_HL, SPACE2
        JP      DUMP0

; ----------------------------------------------

RAB1:   DS      1

GETHEX:                 ; ПОЛУЧИТЬ ИЗ СТРОКИ HEX-ЧИСЛО
        LD      B,H     ; ZF=1 ЕСЛИ O'KAY
        LD      C,L
        XOR     A
        LD      (RAB1),A
        CALL    DC16
        LD      A,(RAB1)
        OR      A
        RET

DC16:   LD      HL,0000
        JP      DC161           ; JMPR

DC163:                          ; ОШИБКА
        LD      A,0FFH
        LD      H,A
        LD      (RAB1),A
DC161:  LD      A,(BC)
        OR      A               ; CP 0DH
        RET     Z
        INC     BC
        CP      20H
        RET     Z
        INC     H
        JP      Z,DC163         ; JMPR
        DEC     H
        CALL    DC8
        JP      C,DC163         ; JMPR
        ADD     HL,HL
        ADD     HL,HL
        ADD     HL,HL
        ADD     HL,HL
        ADD     A,L
        LD      L,A
        JP      DC161           ; JMPR

;* РАСПАКОВКА 8-Р. ; CY=1 - ERROR

DC8:    SUB     30H
        RET     C
        CP      0AH
        CCF
        RET     NC
        CP      11H
        RET     C
        SUB     7
        CP      10H
        CCF
        RET

;--------------------------------------------

NEWLIN: CALL    CR_HEX
SPACE2: CALL    SPACE
SPACE:  LD      A,20H
        RST     10H
        RET

;--------------------------------------------

HEXHL@: CALL    HEX_HL          ; HEX_HL и пробел
        JP      SPACE           ; JMPR

;--------------------------------------------

CR:     EMIT    13
        LD      A,10
        RST     10H
        RET

;--------------------------------------------

CR_HEX: CALL    CR              ; Вывод <ВК> и HL в HEX-виде
HEX_HL: LD      A,H
        CALL    HEX_A
        LD      A,L
HEX_A:
        PUSH    AF
        RRCA    
        RRCA    
        RRCA    
        RRCA    
        CALL    NIBBLE
        POP     AF
NIBBLE:
        AND     0FH
        CP      10
        CCF
        ADC     A,30H
        DAA
SCOUTA:
        PUSH    BC
        LD      C,A
        CALL    MCOUT
        POP     BC
        RET
                
; ----------------------------------------------

SKBDIG: CALL    SKIPBC          ; BC -> HL
HDIGIT: CALL    SKIPHL
        CALL    GETHEX
        RET     Z
PRMER1: POP     AF
PRMERR: RST     18H
        defb    'BAD PARM',0
        RST     38H             ; на CCP

; ----------------------------------------------

SKIPBC: LD      H,B
        LD      L,C
SKIPHL:
        LD      A,(HL)
        CP      20H
        JP      Z,SKIP1         ; JMPR
        CP      9
        RET     NZ
SKIP1:  INC     HL
        JP      SKIPHL          ; JMPR

;--------------------------------------------

STROKA: defb    ' F803',0

;--------------------------------------------

        ROVNO

LENGTH  EQU     $-RABADR

        .dephase
        
        END

Вот функция GETLIN, ввод строки символов с клавиатуры. Она также полный аналог аналогичной программы на ассемблере. И также в отличие от аналогичных п/подпрограмм отечественных ЭВМ имеет почти полноценный редактор строки и тимплет символов. При вводе строки нажимая <забой> или <курсор влево> можно последовательно удаляя символы слева от курсора дойти до места опечатки и исправить, но в отличие от, например, подпрограммы ввода в бейсике РК, не требуется заново вводить удалённые символы, по нажатию <курсор вправо> удалённые справа символы восстанавливаются.

Тимплет символов это память на предыдущую команду, которую ввели этой же процедурой. Строка из тимплета вставляется по нажатию клавиши <курсор вверх> в нулевой позиции строки. Например, Вы вводите команду, запускаете, но она не срабатывает из-за опечатки. Можно не набирать всю строку заново, а вызвать строку из тимплета и отредактировать. Это экономит время, в DOS и нортонах я использовал только такую GETLIN. Кроме того, GETLIN позволяет определить, что была нажата клавиша искейп (в терминологии РК это АР2) или клавиша <F4> или Control-C, т.е был выполнен аварийный выход для прерывания ввода. Например, когда в нортоне пользователь вдруг передумал переименовывать файл. При нажатии АР2 возращается число 255, при Control-C - 254, а при нормальном завершении ввода нажатием <ВК> возвращается число введённых символов.

getlin.pas:

Код:
function getlin(bufadr: ukchar): byte;
const   key_up=$19;
        key_dn=$1A;
        key_lf=8;
        key_rt=$18;
        key_zb=$7F;

var buf,n,i,j: integer;
    ch: char;
    bb: byte;
    ukch: ukchar;

procedure zaboj;
begin
  if n=0 then beep
  else
    begin
      cout(chr(8)); space; cout(chr(8)); n:=n-1;
  end;
end;

begin { getlin }
    buf:=bufadr; ch:=chr(255); n:=0;
    fillchar(buf,21,chr(0));
    while ch<>chr(13) do
      begin
        ch:=conin;
        if n=0 then if ch=chr(key_up) then
           begin
              move(timplet,buf,21);
             poke(buf+21,0);
             ch:=chr(0); cr; presskey;
           end;

        if ch=chr(key_rt) then if n<19 then
           begin
              bb:=peek(buf+n); if bb<>0 then ch:=chr(bb);
           end;
        if ch=chr(key_lf) then ch:=chr(key_zb); bb:=ord(ch);
        if bb>31 then if bb<>key_zb then if n<20 then cout(ch);

        if bb<32 then
           if bb<>13 then beep else bb:=bb
        else                                    { else if bb<32 }
           if bb=key_zb then
              zaboj
           else                         { else bb=key_zb }
              begin
                if n=20 then
                   beep
                else
                   begin
                     poke(buf+n,ch); n:=n+1;
                end;
        end;                            { else if bb<32 }
      end;                              { while }

    poke(buf+n,0); for i:=n to 21 do poke(buf+i,0);
    move(buf,timplet,21);

    case bb of
      27: getlin:=255;
      03: getlin:=254;
    else
      getlin:=n;
    end;
end;                                    { getlin }


А вот то же самое на ассемблере (это для ОРИОНА с Z80, но годится и для других, если соответственно изменить служ.ячейки ПЗУ).

getlin.asm:

Код:
; ------------------  GETLIN  --------------------

BUF_SZ  EQU     32
BAKSTR: DS      BUF_SZ          ; ТИМПЛЕТ буфер

;RI_KEY EQU     'D'-40H         ; если драйвер с подменой
;UP_KEY EQU     'E'-40H         ; если драйвер с подменой

RI_KEY  EQU     18H
UP_KEY  EQU     19H

TIMPLET:
;       RST_18
;       defb    1BH,6BH,0       ; вернуть позицию курсора

        defb    21H             ; то же самое для ЭВМ без VT52
TMPKOO: DS      2
        CALL    WRCURS

        RST_18
        defb    1BH,4BH,0       ; очистка строки
        LD      HL,(RAB1)

GETLIN:                         ; ввод строки (до 48 симв) в буфер HL
        LD      B,BUF_SZ
GTLINB:
        PUSH    HL
        LD      (RAB1),HL
        SET0    RUSLAT
LLL1:   LD      (HL),A          ; A=0
        INC     HL
        DJNZ    LLL1

;       RST_18
;       defb    1BH,6AH,0       ; запомнить позицию курсора

        CALL    RDCURS          ; это то же самое для ЭВМ без VT52
        LD      (TMPKOO),HL

        POP     HL

        LD      B,0
OOO_54: CALL    MCONIN          ; если первое нажатие UP_KEY
        CP      UP_KEY          ; то берём строку из тимплета
        JR      NZ,LLL2
        CALL    GETTPL          ; загрузка строки из тимплета

GTLNLOO:                        ; ВЫХОД: B - число символов
        CALL    MCONIN
LLL2:
        OR      A
        JP      Z,HELP0
LLL4:
        CP      1
        JR      Z,VSTAVKA
        CP      9               ; TAB заменим пробелом
        JR      NZ,GETLN1
        LD      A,20H
GETLN1:
        CP      13
        JR      Z,XGTLEXI       ;GTLEXIT
        CP      1BH
        JR      Z,XGTLEXI       ;GTLEXIT
        CP      3
XGTLEXI: JR     Z,GTLEXIT
        CP      8
        JR      Z,GOLEFT
        CP      7FH
        JR      Z,GOLEFT                ; ZABO

        CP      RI_KEY
        CALL    Z,GTLN04

        LD      C,A          ; проверим, что не > BUF_SZ-1 симв.
        LD      A,B
        CP      BUF_SZ-1
        LD      A,13
        JR      NC,GTLEXIT
        LD      A,C
        CP      20H
        JR      C,OSHCOD
        PUSH    AF
        CALL    SCOUTA
        POP     AF
        CALL    TOUPP
        LD      (HL),A
        INC     HL
        INC     B
        JR      GTLNLOO

GOLEFT: LD      A,B
        OR      A
        JR      Z,OSHCOD
        DEC     HL
        DEC     B
        .emit   8
        JR      GTLNLOO

OSHCOD: CALL    ERRTON  ; DLBEEP
        JR      GTLNLOO

GTLN04: LD      A,(HL)          ; берём символ из буфера строки
        OR      A
        RET     NZ
OSHKO:  PUSH    BC
        CALL    ERRTON          ; DLBEEP
        POP     BC
        POP     AF              ; сброс CALL GTLN04
        JR      GTLNLOO

VSTAVKA:
        LD      A,(HL)
        OR      A
        JR      Z,GTLNLOO

        PUSH    BC
        LD      D,H
        LD      E,L
        LD      A,BUF_SZ-2
        SUB     B
        LD      C,A
        LD      B,0
        ADD     HL,BC
        LD      B,H
        LD      C,L
        DEC     HL
VSTVK1: LD      A,(HL)
        LD      (BC),A
        DEC     HL
        DEC     BC
        CALL    CMPDH
        JR      NZ,VSTVK1
        LD      A,(HL)
        LD      (BC),A
        LD      (HL),20H

        LD      D,H
        LD      E,L
        LD      B,0
LLL5:   LD      A,(DE)
        OR      A
        JR      Z,LLL6
        CALL    SCOUTA
        INC     DE
        INC     B
        JR      LLL5

LLL6:   .emit   8
        DJNZ    LLL6
        POP     BC
        JP      GTLNLOO

GTLEXIT:
        LD      (HL),0
        PUSH    AF              ; последний введённый символ
        PUSH    HL              ; сохранить в тимплете
        PUSH    BC
        LD      HL,(RAB1)
        LD      DE,BAKSTR
        LD      BC,BUF_SZ
        LDIR
        RST_18
        defb    1BH,4BH,0       ; очистка строки
        POP     BC
        POP     HL
        POP     AF
        RET

GETTPL:
        LD      HL,(RAB1)
        LD      DE,BAKSTR
        LD      C,BUF_SZ                ; сколько осталось до конца буфера
        LD      B,0             ; счётчик введённых
GTTPL1: LD      A,(DE)
        OR      A
        JR      Z,GTTPL2
        LD      (HL),A
        CALL    SCOUTA
        INC     DE
        INC     HL
        INC     B
        DEC     C
        JR      NZ,GTTPL1

GTTPL2: PUSH    HL              ; заполним остаток буфера нулями
        INC     C
        DEC     C
        JR      Z,GTTPL4
GTTPL3: LD      (HL),0
        INC     HL
        DEC     C
        JR      NZ,GTTPL3

GTTPL4: POP     HL
        LD      (HL),0
        RET

; ----------------------------------------------

TOUPP:  AND     7FH
        CP      20H
        JR      C,NOSYMB
        CP      5FH
        RET     Z
        CP      41H
        RET     C
        AND     11011111B
        RET

NOSYMB: LD      A,'?'
        RET

PS. Исходники выглядят плохо форматированными из-за того, что BB-код для вывода форматированного текста, т.е {code} в этом движке сделан или настроен неправильно. Используется не моноширинный фонт, как требуется по стандарту, а пропорциональный. Потому для просмотра исходников лучше скопировать их из форума в текстовый редактор с моноширинным фонтом.


Последний раз редактировалось: barsik (Пн Ноя 12 2018, 15:35), всего редактировалось 4 раз(а)

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

Пригодная для использования версия Паскаля МТ+

Сообщение  barsik в Ср Ноя 07 2018, 10:22

Хорошая новость для любителей 8-ми разрядного Паскаля и микро-ЭВМ с процессором КР580.

Наконец-то, решилась главная проблема современности для Паскаля-МТ+. А именно проблема меток в ассемблерных инлайн-вставках. Оказалось, что виновата фирма MicroSystems, оригинальный разработчик Паскаля МТ+. Которая разработала этот Паскаль в 1978-80 годах, а в 1981 продала его фирме Digital Research, которая сначала продавала его под своей маркой в неизменном виде с тем же номером версии 5.5. Но в 1983 году Digital Research переработала этот компилятор и выпустила конечную версию 5.61.

Хотя объём всех модулей последней версии 5.61 увеличились всего на 3%, но различие есть и список исправленных ошибок существенный. Также документация переработана и дополнена большим числом примеров. Увы, в СССР попала более ранняя версия от MicroSystems (1980) с соответствующей документацией. С Корвета она попала на самодельные 8-ми разрядки. В этой версии сообщения в коде компилятора были русифицированы. Возникает справедливая претензия к работе советских служб промышленного шпионажа, - почему в страну завезли раннюю версию, а не более качественную последнюю версию. Вот из-за таких глупых ошибок СССР и не смог догнать и перегнать Америку, как планировалось.

Большинство CP/M архивных сайтов содержат и распространяют версию 5.5, лишь на немногих из них есть версия DR 5.61. Так вот оказалось, что все предыдущие версии МТ+ до Digital Research версии 5.61, в том числе и те, что имели хождение в СССР имеют ущербный, а точнее просто дохлый, встроенный ассемблер. Как уже указывал выше, в этих версиях в инлайн ассемблере нет переходов на метки. Т.е символ * обозначающий текущий адрес трансляции, не работает. И от типа процессора КР580/Z80, как можно предположить, это не зависит.

Это явная дохлота. Вряд-ли в версии 5.5 это ещё не реализовано, а лишь описано в документации на будущее. Скорее всего, в более ранних версиях это работало, а в версии 5.5 сдохло. Иначе как объяснить, что это описано и в более ранней документации для версий 5.1 и 5.2. Увы, версий 5.1 и 5.2 не встречал, проверить нельзя. Как бы там ни было все версии 5.5 и от MicroSystems и от Digital Research имеют полудохлый инлайн-ассемблер, в нём не работают относительные метки.

Ранее я пользовался отечественной версией 5.5 с русифицированными сообщениями, т.к по русски мне читать удобнее. Но пытаясь понять в чём дело попробовал и кучу англоязычных версий 5.5, что брал из CP/M-архивных сайтов. Невозможно было предположить, что фирменный пакет продаваемый за 400 USD в 1981 году содержит ошибку. Но недавно скачал версию DR 5.61, чтобы проверить не даёт ли он более эффективный код, чем версия 5.5. И заодно проверил инлайн-ассемблер.

И о чудо... оказалось, что в версии 5.61 встроенный ассемблер работает точно в соответствии с документацией. Т.е в инлайн-ассемблере можно вставлять переходы, задавая относительное смещение с помощью выражения *+N, где N оффсет адресов от текущего.

Таким образом, из-за чьей-то ошибки 40 летней давности я трахался несколько месяцев, изобретая хитроумные методы, чтобы обойти указанный дефект. А виновата именно фирма производитель. Т.к во всех копиях дистрибутива Pascal МТ+ 5.5, что были сохранены пользователями и впоследствии попали в CP/M-архивы, одна и та же дохлота. Это значит, что именно в таком дохлом виде это и продавалась. И только фирма Digital Research это исправила. Так, что советую всем удалить из всех архивов версию Паскаля МТ+ 5.5 - она просто дохлая, непригодная для использования встроенного ассемблера.

Ещё одной приятностью оказалось, заметное сокращение объёма кода при переопределении операторов посимвольного ввода/вывода - @RNC и @WNC. Оказалось, что компилятор очень грамотно использует свою библиотеку. Он добавляет из неё не все функции, а только те, что используются в программе. Потому переопределив операторы @RNC и @WNC, заменив тем самым каждую из них всего на пару команд ассемблера (как видно из нижеследующего спойлера), мы выигрываем в объёме кода ~800 байтов. В итоге минимальный размер программы может быть совсем маленьким, - менее килобайта (и даже всего полкилобайта, если операторов мало).

Сейчас я транслирую Паскаль программы под TSR эмулятором CP/M в MSDOS (т.к у меня Win XP, а она позволяет прогонять MSDOS программы) и потому трачу всего 3 секунды на трансляцию Паскаль-программы (собственно сама трансляция длится 0.1 секунды, 2 секунды тратится на то, чтобы увидев сообщение, что нет ошибок нажать пробел и ещё секунда уходит чтобы нажать на пробел второй раз после успешной линковки). Ещё секунда уходит на конверсию COM-файла в ORD и загрузку эмулятора ОРИОНА. Т.е собственно на компиляцию у меня теперь уходит так же мало времени как и при программировании на ассемблере - в час можно делать ~25 и более раз цикл модификации Паскаль-исходника, трансляции и проверки работы полученной программы.

Теперь после получения ассемблера я снова переписал подпрограммы нижнего уровня уже целиком на ассемблер. Для написания игр для РК можно немного выиграть в скорости, если делать вывод напрямую в экран, что собственно и делали программисты любители. Но они это делали потому что это проще, чем функциями. Особой необходимости в этом не было, скоростей в РК хватало и при работе через ROM-BIOS.

А вот при ЯВУ надо уже экономить ресурс, - Паскаль, как минимум, раз в 5-6 тормознее, чем ассемблер. Если делать вывод прямой записью в экран и для расчётов экранных позиций использовать табличный метод, то достигается ускорение вывода примерно вдвое. В Паскале с экнаном работать ещё проще, это просто массив sreen[X,Y] и вывод на экран это всего один оператор (все рассчёты выполняются неявно и отпадает утомительное позиционирования курсора перед выводом и чтением байта с экрана).

Можно сразу делать программы универсальными, т.е не только для РК, но и для ОРИОНА или ИРИШИ. Если грамотно выбрать алгоритм, то при этом требуется менять только несколько процедур нижнего уровня. Тогда достаточно включать в линковку модуль для конкретной машины и на выходе без лишних хлопот будут получаться программы для разных ЭВМ, без модификации кода самой программы.

В качестве примера для проверки встроенного ассемблера в вашем компиляторе привожу простейшую программку для определения типа процессора. В ней вывод текстов делается как инлайн-процедурой PRINT (менее 10 команд на ассемблере), так и для демонстрации работы @RNC/@WNC - стандартным паскалевским оператором WRITELN (он намного медленнее и добавляет в код сотню байт). Т.о текст быстрее выводит PRINT или MSSG, зато WRITE позволяет выводить не только текстовые строки, но и десятичные числа (причём с заданным форматом).

определение типа процессора:

Код:

program  CHKCPU;
{$Z7600}

const   wboot= $F86C;
        mconin=$F803;
        mcout= $F809;

{------------------------------------------------}

function is_z80: boolean;
var result: boolean;
begin
  inline( "MVI  A/2/
          "INR  A/
          "MVI A/true/
          "JPO/*+5/
          "MVI A/false/
          "STA/result );
   is_z80:=result;
end;

{------------------------------------------------}

procedure cr;
begin
  inline( "MVI C/13/
          "CALL/mcout/
          "MVI C/10/
          "CALL/mcout );
end;

{------------------------------------------------}

procedure print(str: string);
begin
    inline( "LXI H/str/
            "MOV B,M/
            "INX H/
            "MOV C,M/
            "CALL/mcout/
            "INX H/
            "DCR B/
            "JNZ/*-6 );
end;

{------------------------------------------------}

procedure @wnc(ch: char);
begin
   inline( "LDA/ch/
           "MOV C,A/
           "CALL/mcout );
end;

function @rnc: byte;
var result: byte;
begin
   inline( "CALL/mconin/
           "STA /result );
   @rnc:=result;
end;

{------------------------------------------------}

begin   cr; cr; print('processor ');

        if is_z80 then
           writeln('Z80')
        else
           writeln('KP580BM80');

        inline( "CALL/mconin/
                "JMP/wboot );
end.


Рассматривая CP/M-компиляторы, что пригоды для генерации кода КР580, я не упомянул один ЯВУ, который имеет шанс оказаться лучшим, чем Паскаль, а именно Ада, разработанный в начале 80-тых на базе Паскаля (в этом участвовал тот же автор Н.Вирт). Ясно, что переход от Паскаля к Аде несложен, языки лексически похожи. Для нас удачно, что, например, в Суперсофт-Аду встроен ассемблер и код она генерит для КР580. Я уже подумывал ей заняться. Но теперь, когда нет препятствий к использованию ассемблера с Паскалем, в Аде пока нет нужды (глупо метаться между ЯВУ). Позже будет интересно сравнить качество компиляторов Ады и Паскаля, т.к компилятор Ады написан на 3-5 лет позже, возможно он эффективнее.

Кстати, в CP/M кроме бейсика, Си и Паскаля есть ещё несколько компиляторов старых и даже более новых ЯВУ, являющихся производными от Паскаля. А вот от Си ничего производного нет, т.к С++ возник позже, да и похоже объёма ОЗУ 8-ми разрядки для ООП просто не хватило.

В качестве старых ЯВУ для которых есть CP/M-компиляторы можно упомянуть PL/M, PL/1, Алгол и Фортран (не считая много других экзотических ЯВУ). Два последних, это математические языки, они не годятся для работы по железу. PL/M очень близок к ассемблеру и потому как ЯВУ он не очень мощный (хотя до появления КР580-компиляторов Паскаля в 1978, был популярен). Увы, для PL/M есть единственная книга на русском и её не скачать, можно только купить. Совсем древний PL/1 из 1964 немного похож и на PL/M и на Паскаль и скорее всего вполне подойдёт для разработки ПО для РК86. Важно, что литературы по PL/1 больше, чем для PL/M и его компилятор для КР580 кажется лучше, т.к более свежий, чем компилятор PL/M. Как упомянуто ранее, есть несколько CP/M-компиляторов Ада для КР580. Есть и современные кросс-компиляторы языков Ада, Модула-2 и Оберон (они, кстати, Паскаль-производные), но для РК они, увы, не годятся, т.к выдают код для Z80.

И всё же из этих ЯВУ для 8-ми разрядок наиболее удобным для программирования для ретро-ЭВМ следует считать Паскаль МТ+ и два Си, BDS и Aztec. Может они и не самые эффективные, но по ним наибольший объём литературы и документации и большое число примеров, конкретно для данных компиляторов. В частности, для Паскаля МТ+ в 80-тые годы существовала BBS MT+ Users Group, от которой сохранился архив содержащий ~300 исходников программ написанных любителями на Паскале МТ+.

Хорошая история Паскаля МТ+ вот здесь (там кстати, написано, что компиляция большой программы на дисководе занимала часы, что хоть и преувеличение, но сама идея о тормознутости верна). Там также указано ("Pascal/MT being decidedly superior in most ways to early Turbo Pascal products...), что Паскаль МТ+ лучше, чем Турбо, который вообще можно считать лишь учебной игрушкой. А также, что он лучше, чем BDC C.

Вот для примера использования ассемблера с переходами ещё несколько процедур полезных при разработке программ для РК86.

процедуры только для РК86:

Код:

const
        mconin= $F803;
        mcout=  $F809;
        mstat=  $F812;
        xf81b=  $F81B;
        mhex_a= $F815;
        mmssg=  $F818;
        askcur= $F81E;
        rd_scr= $F821;
        chsumm= $F82A;
        asktop= $F830;
        settop= $F833;
        wboot=  $F86C;
        cmpdh=  $F990;

        key_up= $19;
        key_dn= $1A;
        key_lf= 8;
        key_rt= $18;
        key_zb= $7F;
        
        STACK=  $76D0;          { стек монитора }
        SA=     $76D0;          { начало экранной области }
        SCBASE= $77C2;          { левый верхний угол SA+78*3+8 }
var
        scradr: absolute [$7600] integer;       { текущий адрес на экране }
        posx:   absolute [$7602] byte;          { горизонтальная }
        posy:   absolute [$7603] byte;          { вертикальная }
        posxy:  absolute [$7602] integer;
        escflg: absolute [$7604] byte;
        kbdflg: absolute [$7605] byte;          { если не 0, то есть символ в SYMBUF }
        ramtop: absolute [$7631] integer;
        combuf: absolute [$7633] integer;

        sndparm: integer;

        screen: absolute [$77C2]
                array [0..24] of
                array [0..77] of char;
          
{------------------------------------------------}

procedure sound;
begin
   inline(                        
        "LHLD/sndparm/  { sound:LD HL,(sndparm) }
        "XCHG/          {       EX      DE,HL   }
        "MOV A,D/       { BP1:  LD      A,B     }
        "EI/            { BP2:  EI              }
        "DCR A/         {       DEC     A       }
        "JNZ/ *-2/      {       JP      NZ,BP2  }
        "MOV A,D/       {       LD      A,B     }
        "DI/            { BP3:  DI              }
        "DCR A/         {       DEC     A       }
        "JNZ/ *-2/      {       JP      NZ,BP3  }
        "DCR E/         {       DEC     C       }
        "JNZ/ *-13/     {       JP      NZ,BP1  }
        "RET );         {       RET             }
end;

{------------------------------------------------}

procedure zwuk(parm: integer);
begin
   inline(                        
        "LHLD/parm/
        "SHLD/sndparm );
   sound;
end;

{------------------------------------------------}

procedure cls;
begin
   inline(
        "LXI H/SA+2340/         {      LD   HL,SA+30*78 }
        "LXI B/2340 +1/         {      LD   BC,30*78 +1 }
        "MVI M/0/               { LOO: LD   (HL),0      }
        "DCX H/                 {      DEC  HL          }
        "DCX B/                 {      DEC  BC          }
        "MOV A,C/               {      LD   A,C         }
        "ORA B/                 {      OR   B           }
        "JNZ/*-6 );             {      JP   NZ,LOO      }
   home;
end;

{------------------------------------------------}

procedure home;
begin
   inline(
        "LXI H/$308/
        "SHLD/posxy/
        "LXI H/SCBASE/
        "SHLD/scradr );
end;

{------------------------------------------------}

procedure roll;                 { РОЛИК ЭКРАНА }
begin
   inline(
        "LXI H/$7810/           {         LD    HL,SCBASE+78   }
        "LXI D/SCBASE/          {         LD    DE,SCBASE      }
        "LXI B/1950/            {         LD    BC,25*78       }
        "MOV A,M/               { ROLLOO: LD    A,(HL)         }      
        "STAX D/                {         LD    (DE),A         }
        "INX H/                 {         INC   HL             }
        "INX D/                 {         INC   DE             }
        "DCX B/                 {         DEC   BC             }
        "MOV A,C/               {         LD    A,C            }
        "ORA B/                 {         OR    B              }
        "JNZ/*-7 );             {         JP    NZ,ROLLOO      }
end;

{------------------------------------------------}

                                { а вот ролик попроще, но не быстрее }
procedure roll;
var line1: absolute [$7810] byte;
    line0: absolute [$77c2] byte;
begin
        move(line1, line0, 1950);
end;

{------------------------------------------------}

procedure rolldn;               { РОЛИК ВНИЗ }
begin
   inline(
        "LXI H/$7F11/           {         LD    HL,SCBASE+78*24-1 }
        "LXI D/$7F5F/           {         LD    DE,SCBASE+78*25-1 }
        "LXI B/1872/            {         LD    BC,24*78        }
        "MOV A,M/               { ROLLOO: LD    A,(HL)          }      
        "STAX D/                {         LD    (DE),A          }
        "DCX H/                 {         DEC   HL              }
        "DCX D/                 {         DEC   DE              }
        "DCX B/                 {         DEC   BC              }
        "MOV A,C/               {         LD    A,C             }
        "ORA B/                 {         OR    B               }
        "JNZ/*-7/               {         JP    NZ,ROLLOO       }
        "LXI H/$77C2/           {         LD    HL,SCBASE       }
        "MVI B/70/              {         LD    B,64+6          }
        "MVI M/0/               { LOOP2:  LD    (HL),0          }
        "INX H/                 {         INC   HL              }
        "DCR B/                 {         DEC   B               }
        "JNZ/*-4 );             {         JP    NZ,LOOP2        }
end;

{------------------------------------------------}

procedure hex(byt: byte);
begin
   inline(
        "LDA/byt/
        "PUSH P/
        "RRC/
        "RRC/
        "RRC/
        "RRC/
        "CALL/*+4/
        "POP PS/
        "ANI/$0F/
        "CPI/10/
        "CMC/
        "ACI/$30/
        "DAA/
        "MOV C,A/
        "JMP/$F809 );
end;


Для конверсии больших фрагментов кодов в текст использовал вот этот конвертор в данные для бейсика. Полученный дамп для инлайн-вставок Паскаля надо немного редактировать. Эту программу не изменить, исходника нет. Позднее на Паскале напишу её вариант, что генерит дамп сразу в формате инлайн-вставки.

_________________
***
avatar
barsik
новичёк

Сообщения : 90
Дата регистрации : 2016-11-10
Откуда : 600 км от Москвы

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Использование ЯВУ для разработки программ для РК86

Сообщение  Спонсируемый контент


Спонсируемый контент


Вернуться к началу Перейти вниз

Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения