nedoPC.org

Community for electronics hobbyists, established in 2002
Atom Feed | View unanswered posts | View active topics It is currently 11 Dec 2024 21:45



Reply to topic  [ 31 posts ]  Go to page Previous  1, 2, 3  Next
Программирование на компиляторах ЯВУ для ретро ЭВМ 
Author Message
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Появился небольшой опыт в написании программ для КР580 для систем без DOS CP/M на Паскале МТ+. Этот Паскаль написан в 1978...1980 году, причём не одним человеком (как было с Си Aztec и BDS, которые написали частники), а фирмой, т.е сразу многими программистами. Потому уровень этого Паскаля высокий. К сожалению, разработчики стремились совместиться со стандартом Паскаля того времени, потому на 95% реализован классический Паскаль, описанный в документах Н.Вирта.

Для сравнения Турбо-Паскаль появившийся 5 лет спустя, ввёл много мелких полезных доработок, нестандартных, но упрощающих программирование (и впоследствии ставших стандартом). Например, в МТ+ нельзя ввести в символьную строку управляющие символы, что без труда делается в TP (например, #13#10'Привет' или ^]^Y#32#32), метки для переходов - только цифровые (как в бейсике), а не символьные, как в TP и других развитых ЯВУ, узнать адрес можно только для переменной или процедуры расположенной в том же самом файле (для других модулей это уже не работает). Встроенные инлайн-ассемблер (понимающий HEX-дампы и мнемонику КР580) не допускает меток, что существенно снижает его ценность. Объём и, соответственно, скорость генерируемого кода, как общепризнано, хуже, чем у TP. Есть ещё много других мелких неприятностей. Компиляция длится на порядок дольше, чем в TP. Вообще программирование на этом Паскале очень сильно отличается от программирования в борландовских продуктах на PC и GNU Pascal Compiler в Linux. Но у МТ+ есть неоспоримое преимущество перед Турбо-Паскалем, это линковка модулей и процессор КР580. TP очень хорош, но из-за нелинкующего принципа для медленных ЭВМ он имеет низкую полезность и по сути может считаться игрушкой.

Мне и ранее было известно, что МТ+ генерит менее эффективный код, чем BDS Си, AZtec Си и Турбо-Паскаль. Например, Турбо-Паскаль даёт объём кода примерно в 4 раза больший чем полный аналог написанный на ассемблере. Но тут я специально сравнил МТ+ с асссемблером и объём кода на МТ+ на программах в 300-400 строк Паскаля получился в 7-10 раз больший, чем у аналогов написанных на ассемблере.

Вопреки ожиданиям, при трансляции в эмуляторе эффективность разработки на этом компиляторе не стала на порядок быстрее, чем на ассемблере. Тормозит медленная трансляция в эмуляторе 8-ми разрядки.

Если при программировании на ассемблере при каждой перетрансляции технические потери составляют всего секунду (хлоп по кнопке и можно проверять прогон в эмуляторе), то трансляция в эмуляторе CP/M-компилятором 1000 строк длится 6-8 минут, хотя потерь времени на запуск эмуляторов ОРИОНА или РК86 для проверки нет, т.к и транслируется в эмуляторе ОРИОНА. С реальным дисководом время трансляции - вдвое выше. А для проверки программ для РК86 запускается эмулятор РК86 в эмуляторе ОРИОНА (т.е происходит двойная эмуляция: PC эмулирует ОРИОН, ОРИОН эмулирует РК). А ещё одной причиной тормознутости является то, что за одну итерацию находится и исправляется только одна ошибка, т.к встретив ошибку компилятор останавливается. А ассемблер сразу выдаёт кучу ошибок и их все можно исправить зараз, за одну итерацию.

Улучшить свойства программы на Паскале можно заменив в РК86 процессор на Z80. Это устраняет проблему отсутствия меток в инлайн-ассемблере, т.к у Z80 есть JR-команды. И хотя встроенный в МТ+ инлайн-ассемблер не понимает мнемоники Z80, но можно совместимые команды писать в мнемониках КР580, вставляя вместо JR-команд просто байты. Чтобы не трахаться с набором ассемблерных текстов, удобнее транслировать ассемблерные вставки на Z80 в обычном ассемблере, генерировать дамп и в ассемблерные функции и процедуры в Паскаль-программе вставлять странслированные дампы, а не набирать текст в мнемониках.

К сожалению, похоже Microsoft не написала компилятор Паскаля для КР580/Z80, а только компилятор фортрана. Можно попробовать компилятор фортрана. На нём удобно написать программу рассчёта траектории полёта на Марс, но для написания игр для РК86 он вряд-ли будет удобен, т.к в нём нет указателей, ассемблера и невозможно работать по железу.

Интересно есть-ли кросс-компилятор Паскаля работающий в MSDOS или Windows на PC, выдающий на выходе код для процессора КР580/Z80 ?

Для компьютера Полдин-601 болгары написали компилятор Паскаля в двух версиях - одна в кодах CPU 6800 работает на реале, а вторая в кодах 8086 работает на PC в MSDOS. Если такого компилятора нет, то остаётся только заменять КР580 на 6802 в РК86 или же использовать конвертор Паскаль-программ в программу на Си, а далее транслировать в коды КР580 с помощью кросс-компилятора Си.

Увы, это тоже не варианты, т.к 6802 для нас непривычный и заметно уступает КР580 по производительности (при том же цикле доступа к ОЗУ). А использование конвертора Паскаль в Си громоздко, чревато доп.ошибками, а главное, всё-равно бесполезно. Встретил лишь единственный кросс-копилятор Си в коды КР580, это компилятор от vinxru, но он не стандартный, а документации нет. Выглядит так, как будто на Западе вообще никто не писал кросс-компиляторов для КР580 и похоже, что вне пределов бывшего СССР процессор КР580 непопулярен.

- - - Добавлено - - -

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

В качестве старых ЯВУ надо упомянуть ЯВУ PL/M и PL/1. Первый очень близок к ассемблеру и считается, что как ЯВУ он не очень мощный (хотя до появления компиляторов Паскаля в конце 70-тых, был популярен). Более древний PL/1 немного похож и на PL/M и на Паскаль и скорее всего вполне подойдёт для разработки ПО для РК86. Его компилятор для КР580 выглядит солиднее, чем компилятор PL/M. Есть ещё несколько CP/M-компиляторов АДА для КР580. Это язык производный от Паскаля и, очень похоже, что его тоже вполне можно использовать, т.к он имеет встроенный ассемблер. Есть и кросс-компиляторы языков АДА и ОБЕРОН, но они тоже, увы, лишь для Z80.


Last edited by barsik on 06 Nov 2018 22:03, edited 2 times in total.



27 Oct 2018 07:15
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post Re:
barsik wrote:
Интересно есть-ли кросс-компилятор Паскаля работающий в MSDOS или Windows на PC, выдающий на выходе код для процессора КР580/Z80 ?

Есть такой, и похоже, что даже не один, если хорошо поискать...
Скачал чисто для проверки, не битая ли ссылка:
Attachment:
Pascal.gif
Pascal.gif [ 7.8 KiB | Viewed 8147 times ]

Но поскольку мне он не нужен, то удалю...

_________________
iLavr


31 Oct 2018 05:05
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Lavr wrote:
есть под Микрошей отладчик DDT - он запускался с адреса 100Н, что тогда было странно, т.к. в основном программы грузились с 0. Я поковырял DDT и выяснил, что ниже 100Н, он формирует при старте некую структуру, где есть переходы на системные подпрограммы ПЗУ. Интересно, что этот отладчик DDT понимал коды Z80, хотя и был в софте для Микроши.
Если бы программисты для Микроши читали журнал МПСС, то им бы не потребовалось адаптировать DDT CP/M. Это сделали задолго до этого разработчики ИРИШИ и очистив DDT от ненужных дисковых функций, доработали его и опубликовали в журнале МПСС в 1986 году. Насчёт кодов Z80 в DDT. Видимо речь о ZDDT, это универсальная версия DDT, что работает на КР580.

Раз этот DDT был с адреса 100, значит это вообще неадаптированный DDT. Мне среди ПО РК попадались лишь уже адаптированные версии DDT (уже перемещённые под вершину ОЗУ). В оригинальном DDT в его начале стоит процедура переноса кода под вершину TPA по таблице коррекции. Для получения кода DDT работающего в вершине ОЗУ ставят стоп точку (или JMP F86C) в конце процедуры переноса и подставив в адрес 6 значение из ячейки RAMTOP, чтобы задать TPA, делают запуск с адреса 100. После чего код DDT настроенный на адреса берётся из вершины ОЗУ, а для завершения адаптации остаётся в полученном коде, уже настроенном на верхние адреса, найти все CALL 5 для консольных вызовов, и заменить их на CALL F803/F809.

Но можно этого не делать, а использовать код CP/M-программы в оригинале, добавив к ней частичный эмулятор BDOS CP/M. Причём для недисковой программы эмулировать требуется только консольные функции - 1, 2, 6, 9 и 10.

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

Эмулятор функций BDOS перехватывает вызовы из программ командой CALL 5. Для этого на адрес 5 ставится или JMP в эмулятор BDOS размещённый в вершине ОЗУ или размещённый здесь же (если он умещается в адресах 5...FF). Такой программе достаточно эмулировать лишь недисковые функции BDOS CP/M, что позволяет использовать программы CP/M до тех пор пока они не лезут к диску.

Программы полученные из под компиляторов ЯВУ, например Паскаля, являются полноценными CP/M-программами, в которых стандартные фунции READ и WRITE для ввода/вывода на консоль выполняют вызов BDOS по адресу 5. Потому для написания на ЯВУ программ для РК86 работающих без CP/M операторы READ/WRITE непригодны для ввода/вывода и можно использовать только специально созданные процедуры написанные на встроенном ассемблере. Которые очень просты, т.к они делают только вызов стандартных подпрограмм ПЗУ F803 и F809.

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

Чтобы без модификации кода компилятора или переопределения некоторых процедур низкого уровня RUN-тайм библиотеки, работали стандартные операторы READ и WRITE как раз годится эмулятор консольных BDOS-функций. Это простейшая программка размером всего в 256 байт и я ещё в начале 90-тых написал такой эмулятор для адаптации отладчиков. Только я грузил его не в Zero-Page, а под вершину ОЗУ, чтобы можно было отлаживать программы с адреса 0. И этот же код годится для того, чтобы на без-CP/M-ном РК86 или ОРИОНЕ можно было использовать программы написанные на ЯВУ.

Для использования на РК программ созданных для CP/M, в том числе и написанных на Паскале, удобно в свободный участок ПЗУ, например, в адреса F000...F7FF прошить эмулятор консольных функций CP/M (естественно, перетранслировав на соответствующие адреса).

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

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

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

;  Эмулятор консольных функций BDOS CP/M. Может располагаться в Zero-
;  Page CP/M (т.к имеет размер всего в 255 байтов). Этот код в 256 байт
;  просто подставляется в начало бездисководной CP/M-программы, после
;  чего программа должна стартовать с адреса 0 и сможет работать без
;  CP/M. Если Zero-Page нужна для RST, то можно грузить этот BDOS под
;  вершину ОЗУ (на 7500), а удобнее всего прошить в ПЗУ в области F000.
 
       .Z80
       aseg
       ORG     100H
       
MYBDOS  EQU     75F6H

       public  MYBDOS

MCONIN  EQU     0F803H
MCOUT   EQU     0F809H
MSTAT   EQU     0F812H
XF81B   EQU     0F81BH
WBOOT   EQU     0F86CH

STACK   EQU     076D0H

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

LOOP    MACRO   ADDR
       DEC     BC
       LD      A,B
       OR      C
       JP      NZ,ADDR
       ENDM

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

       .phase  0
       JP      OOO
       DS      2
       JP      MYBDOS

OOO:    LD      HL,WBOOT
       LD      (1),HL

       LD      A,0C3H
       LD      (MYBDOS),A
       LD      HL,XBDOS
       LD      (MYBDOS+1),HL

.comment \
       LD      HL,DATA
       LD      DE,MYBDOS
       LD      BC,@LENGTH
@LDIR:  LD      A,(HL)
       LD      (DE),A
       INC     HL
       INC     DE
       LOOP    @LDIR
\
       JP      100H
DATA:

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

;       .dephase
;       .phase  MYBDOS

XBDOS:  LD      HL,0
       ADD     HL,SP
       LD      (TMPSTK),HL

       LD      SP,STACK
       LD      A,C
       OR      A
       JP      Z,0             ; WBOOT ; функция 0 (WARM BOOT)

       CP      12              ; ф.12 номер версии
       JP      NZ,JJJ_01
       LD      A,22H
RETURN:
       defb    21H             ; LD HL,(TMPSTK)
TMPSTK: DS      2
       LD      SP,HL
       LD      L,A
       LD      H,0
       RET

JJJ_01: LD      HL,RETURN
       PUSH    HL

       LD      C,E
       CP      1
       JP      Z,FCONIN        ; F.1 CONIN
       CP      2
       JP      Z,MCOUT         ; F.2 CONOUT
       CP      6
       JP      Z,FUNC_6        ; F.6 direct I/O
       CP      9
       JP      Z,MSGCPM        ; F.9 MSSG
       CP      10
       JP      Z,FRDBUF        ; F.10 RD_BUFF
       CP      11
       JP      NZ,WBOOT
NSTAT:
       PUSH    HL              ; F.11 STATUS
       LD      HL,COUNT
       LD      A,(HL)
       OR      A
       JP      Z,NSTA_2
       DEC     A
       LD      (HL),A
       XOR     A
;       JP      NSTA_3
       POP     HL
       RET

NSTA_2: INC     HL
       LD      A,(HL)
       OR      A
       JP      NZ,NSTA_1
       CALL    XF81B
       INC     A
       JP      Z,NSTA_3
       DEC     A
       LD      (KBDBUF),A
NSTA_1:
       LD      A,255
NSTA_3: POP     HL
       RET

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

MSGCPM:
;       PUSH    HL              ; надо, если MCOUT в компьютере портит регистры
;       PUSH    BC
       EX      DE,HL
MSGLOO: LD      C,(HL)
       LD      A,'$'
       CP      C
;       JP      Z,POP_BH
       RET     Z
       CALL    MCOUT
       INC     HL
       JP      MSGLOO

POP_BH:
;       POP     BC
;       POP     HL
;       RET

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

COUNT:  defb    255
KBDBUF: defb    0               ; должны идти подряд

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

FUNC_6: INC     E
       JP      NZ,MCOUT
       CALL    MSTAT
       RET     Z
       JP      MCONIN

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

FCONIN:
;       PUSH    HL              ; надо, если MCONIN в компьютере портит регистры
;       PUSH    BC
       LD      HL,COUNT
       LD      (HL),255
       INC     HL              ; KBDBUF
       LD      A,(HL)
       OR      A
       JP      NZ,CONI_2
       CALL    MCONIN
CONI_2: CP      3
       JP      Z,0             ; WBOOT
       LD      C,A
       PUSH    AF
       CALL    MCOUT
       POP     AF
;       POP     BC
;       POP     HL
       RET

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

FRDBUF:
       EX      DE,HL
       LD      C,(HL)          ; BUFF SIZE
       LD      B,0
       INC     HL
RDBLOO:
       CALL    MCONIN
       CP      8
       JP      Z,ZABOJ
       PUSH    BC
       LD      C,A
       CALL    MCOUT
       POP     BC
       CP      13
       JP      Z,RDDONE
       CP      10
       JP      Z,RDDONE
       CP      3
       JP      Z,0             ; WBOOT ; CNTRL-C
       INC     HL
       LD      (HL),A
       INC     B
       LD      A,C
       CP      B               ; конец буфера ?
       JP      NZ,RDBLOO
       LD      B,C
RDDONE:
       INC     DE
       EX      DE,HL
       LD      (HL),B
       RET

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

ZABOJ:  LD      A,B
       OR      A
       JP      Z,RDBLOO
       DEC     B
       DEC     HL
       PUSH    BC
       LD      C,8
       CALL    MCOUT
       LD      C,20H
       CALL    MCOUT
       LD      C,8
       CALL    MCOUT
       POP     BC
       JP      RDBLOO

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

@ENDBDOS        EQU     $

BDSIZE  EQU     $-MYBDOS
@LENGTH EQU     $-MYBDOS

@FREE   EQU     7600H-$
       
       .dephase

if      low $ and 7FH
       rept    80H-(low $ and 7FH)
       defb    255
       ENDM
endif

       END



06 Nov 2018 02:42
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Тему, к сожалению, увели в сторону и засорили не относящимся к теме флеймом. Но всё-же надо возвращаться к исходной теме, - выбору оптимального компилятора ЯВУ для программирования ретро ЭВМ.
Lavr wrote:
barsik wrote:
Интересно есть-ли кросс-компилятор Паскаля работающий в MSDOS или Windows на PC, выдающий на выходе код для процессора КР580/Z80 ?
Есть такой, и похоже, что даже не один, если хорошо поискать...
Сомневаюсь. Для КР580 нет, т.к похоже, что КР580 вне России мало популярен. И даже для Z80 изображённое на картинке IDE скорее всего полуфабрикатное демо (типа студенческой работы). Возможно для Z80 пригодные кросс-Паскаль-IDE всё-же есть, но платные (есть кросс-компиляторы Паскаля для AVR и ПЛИС, а изменить целевую платформу разработчику нетрудно). Иначе на всех подобных форумах не было бы таких же вопросов и попыток сделать их самому.

Конечно интегрированная среда ускоряет разработку. Это чётко показал ещё си-пи-эм-овский Турбо-Паскаль 3.0 (но он, увы, для Z80). А пока, если используется Win XP (что позволяет запуск MSDOS программ), то используя MSDOS TSR-эмулятор CP/M и Паскаль МТ+, потери времени на компиляцию снижаются почти до нуля, а имеющийся в нём отладчик, хотя и не так удобен как встроенный в IDE, но тоже упрощает отладку. Так что эффективность разработки, относительно разработки в среде кросс-IDE, страдает не существенно.
barsik wrote:
не могу разобраться как в ассемблерных вставках задавать адреса в командах переходов (а ассемблер без переходов почти бесполезен)
Наконец-то и эта неприятная проблема успешно разрешилась. Совсем идеально с ассемблером было бы, если бы встроенный инлайн-ассемблер использовал мнемоники Z80 вместо устаревших мнемоник КР580... но всё идеально бывает только в мечтах.

Освоение Паскаля начинающим, особенно после бейсика, по сравнению с освоением Си, происходит намного легче и быстрее. Сужу по себе, в начале 90-тых пытался освоить Си, но не преуспел за много месяцев. Тогда как бейсик-компилятор был освоен мгновенно. После чего и Турбо-Паскаль был освоен всего за две недели. Позднее, я больше имел дело с Си, но как раз потому, что Паскаль (хотя и не МТ, а Турбо) был моим первым ЯВУ, отношусь к нему с симпатией.

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

Классический Си ближе к железу, в нём удобнее работа с указателями, тогда как в классическом Паскале из 70-тых указатели используются в основном для работы с динамическим объектами (позже в TP работу указателей улучшили). В Си нет строгой несовместимости похожих типов, как в Паскале, что защищает от ошибок, но снижает гибкость. Правда указатели в режиме ослабленного контроля, а тем более встроенный ассемблер позволяют обойти все строгости Паскаля, но это "химия".

Особенно утомляет несовместимость числовых типов с boolean (и true это не любое, отличное от 0, число, а именно 1). Потому нельзя, как в BDS C, в качестве условия в условных операторах написать числовое выражение или даже просто переменную. Работа с файлами существенно отличается от последующих стандартов (заданных Турбо-Паскалем), примеров в описании нет, пришлось выяснять их применение экспериментально. И вопреки нижеследующей переводной цитате, где написано, что МТ+ лучше, чем BDS С, в отличие от МТ+, с BDS у меня лично вообще не было проблем, даже ассемблер для трюков был не нужен (хотя возможно мне просто не встретились упомянутые ниже глюки BDS C).
Quote:
Хотя к настоящему времени в значительной степени забытый, Паскаль-MT+ был в свое время очень влиятельным продуктом ... К 1983 году о Паскале-MT+ было доступно больше книг, чем о всех других компиляторах Паскаля вместе взятых ... Если вы хотите программировать в CP/ M на C в конце 70-х, то у вас был выбор: либо тратить свою жизнь на бесконечные перестановки дисков, открытие и закрытие редакторов, компиляторы, компоновщики и другие утилиты, либо потратить свою жизнь на написание программ в борьбе с несовместимостью и бесконечными ошибками BDS C. Это было одной из тех вещей, которые привели людей из C и к компиляторам, таким как Pascal на небольших микрокомпьютерных системах ...

Первоначально подмножество Паскаля по имени Паскаль-MT+ использовало тот факт, что было гораздо проще написать компилятор для высокоструктурированного языка Pascal, чем для неструктурированных C или классических языков, таких как Fortran, в дополнение к тому, что Pascal намного меньший язык. Написанная на языке ассемблера, она выполняла тот же трюк, что и BDS C, - выполнение компиляции фактически внутри доступной памяти, и в отличие от продукта BDS, система Паскаль-MT+ не содержала ошибок, документация была ясной и лаконичной, и компания активно развивала обновления и исправления ошибок. Компилятор также имел встроенный компоновщик, который автоматически связывал объектный код при компиляции, новшество в то время, имел символический отладчик, редактор и предлагал поддержку наложения оверлеев, в результате чего скомпилированная исполняемая программа загружала в память только ту часть кода, которая была необходима, когда конкретный сегмент программы выполняется.
Паскаль господствовал в программировании почти до конца 80-тых, но как пишут, не столько из-за превосходства Си, сколько из-за того, что Unix и Си поставлялись в университеты бесплатно, проиграл в конкуренции с Си. Как только в промышленность пришли бывшие студенты обучавшиеся на Unix и Си, Паскаль был обречён и даже незаслуженно стал презираемым, почти как бейсик.


09 Nov 2018 00:09
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Хотя в рассмотрение ещё не попал PLI от Digital Research (а Гарри Килдэлл считал его самым эффективным инструментом для процессора 8080), можно подвести кое-какие итоги.

Если доступен только процессор КР580, то считая самым важным критерием хотя-бы возможность написать реальную программу на ЯВУ, побеждает PL/M. Пусть и самый неудобный в разработке, PL/M это ЯВУ дающий реальный шанс написать программу для 8-ми разрядки на ЯВУ. Про другие ЯВУ такого не скажешь (если не считать программами крошечные поделки). PL/M побеждает по плотности кода практически без конкуренции. Он даёт плотность кода лишь в 1.5 раза худшую, чем ассемблер. В этой гонке ближайшие конкуренты Паскаль и Си остались далеко за горизонтом.

Сейчас PL/M совсем не популярен. Применению PL/M у отечественных любителей помешало его отсутствие, как класса, т.к компилятор имелся лишь для ОС ISIS (СМ-1800), а на отечественных самоделках этой ОС не было. Впрочем и других ЯВУ на отечественных бытовых ЭВМ в периоды их массового использования (из-за отсутствия дисководов) тоже практически не было, - дисководы, DOS и компиляторы ЯВУ стали доступны слишком поздно, в основном в 1993 году, после заката популярности бытовых и любительских ЭВМ.

Но сейчас ситуация изменилась. Недавно был открыт компилятор PLMX и каким-то людям удалось конвертировать на Си, странслировать и научиться использовать на IBM PC, исходно работающий на майн-фрейме, оригинальный компилятор PL/M. Ещё раз подчеркну, что хоть разработка на PL/M и не так удобна, как на других ЯВУ (некоторые из которых имеют даже IDE), но она хотя-бы даёт реальный шанс написать на ЯВУ программу, которую ранее можно было сделать только на ассемблере.

При КР580 конкуренты PL/M по плотности кода значительно отстают (ближайший конкурент Паскаль МТ+ хуже, чем PL/M по плотности выходного кода аж в ~3 раза). Но для Z80 ситуация лучше. После PL/M самым эффективным по объёму кода, а уж тем более по скорости разработки, является Турбо-Паскаль. Судя по моему небольшому опыту с TP, он даёт разбухание объёма кода (относительно голого ассемблера) примерно в 3 раза (естественно это не точная величина, для каждой программы это соотношение своё). Хотя это вдвое хуже, чем у PL/M, но это с избытком компенсируется на порядки большей скоростью разработки программ (примитивное IDE в TP даёт даже возможность отладки).

На третьем месте в кандидаты на оптимальное ЯВУ для ретро любителя будет какой-либо Си, с коэффициентом разбухания кода и торможения ~4. Кстати, современные версии кросс-Си для Z80 всё ещё сопровождаются разработчиками и даже развиваются. Возможно они уже могут конкурировать по плотности кода с Турбо-Паскалем (хотя по удобству, скорости разработки точно нет).

Для машин на КР580 Турбо-Паскаль, т.к он для Z80, выпадал из рассмотрения. Хотя PL/M даёт вдвое лучшую плотность кода, чем Турбо-Паскаль, но сейчас гораздо важнее скорость разработки. А в этом Турбо-Паскаль на порядок, если не на два эффективнее, чем PL/M. Потому при Z80, если важны удобство программиста и скорость разработки (т.е если нужен результат, а не просто поупражнять мозги), то вывод очевиден, - альтернативы Турбо-Паскалю нет. И лишь, если объём кода разбух выше допустимого, а замена части процедур на ассемблер не помогает, то придётся использовать PL/M. К сожалению, включение процедур на ассемблере в TP менее удобно, чем в компиляторах с линковкой. Это плата за скорость компиляции.

По совокупности параметров, возможно, что все эти 3 варианта примерно равноценны (другие преимущества Си или Паскаля уравнивают потерю в объёме кода относительно PL/M). Я занялся PL/M лишь оттого, что ранее мне был нужен код для КР580, но сейчас из-за изменившихся обстоятельств ограничение в выходном коде именно под КР580 исчезло. Кстати, при использовании PL/M с Z80 полезна программа оптимизатор, которая сканирует исходник в кодах Z80 и отыскивает команды JP, которые можно заменить на JR (в идеале даже переставляя фрагменты текста, чтобы увеличить процент команд JR). Почему никто не встроил такую функцию оптимизации в ассемблер, это же совсем несложно? А умный конвертор мог бы оптимизировать под JR даже программу в кодах КР580.

Если же целевой процессор 6502, то для него нет ни PL/M, ни Турбо-Паскаля (да и других Паскалей нет). Для него пригоден лишь третий по эффективности вариант - использование кросс-компилятора Си CC65.


01 Nov 2019 11:51
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
А откуда все эти данные про "по плотности выходного кода аж в ~1.5 раза)... ~3 раза)... ~4 раза"? :o

_________________
iLavr


01 Nov 2019 17:03
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Lavr wrote:
А откуда все эти данные про "по плотности выходного кода аж в ~1.5 раза... ~3 раза... ~4 раза"?
Из моих предыдущих постов ответ на этот вопрос любому совершенно ясен.

Специально для вас ещё раз уточняю, что с Паскалями и Си для CP/M я имел дело сам и могу судить. К тому же я чётко указал, что это примерные лично мои данные, и скорее всего даже завышенные (в пользу компиляторов Си и Паскаля). Оригинальный PL/M я не тестировал (лишь PLMX, который я думаю не хуже), но в википедии написано, что PL/M ещё эффективнее, чем я пишу, а разработчик PL/M приводил в своих статьях ещё более лучшие данные по плотности кода. Более точные данные можно получить лишь специально сравнивая одну и ту же программу, лишь реализованную разными языками. Да и тогда данные не будут точными, потому что для разных программ соотношение объёмов кода будет разное.

Эффективности современных кросс-компиляторов Си под Z80 я не знаю, потому и не приводил. Интересно было бы сравнить. У меня есть исходник моей программы в 3000 строк на BDS C и ассемблере, но сравнить с другими Си без долгого траха не получится, т.к это не чистый Си (много ассемблера, а механизмы его включения у разных компиляторов разные), да и есть отличия в синтаксисе, объёме реализованных операторов и их работе.


01 Nov 2019 21:18
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
При 8-ми разрядке на процессоре Z80 при выборе компилятора ЯВУ почти нет альтернативы Турбо-Паскалю. Именно поэтому Паскаль чуть не победил Си в 80-тые годы, он был популярнее, чем Си. Лишь когда Си породил C++, а Паскали с поддержкой ООП опоздали, то в конце 80-тых Си стал популярен, а к середине 90-тых уже сильно потеснил Паскаль, хотя Дельфи позволил Паскалю ещё долго оставаться полноценным ЯВУ для Windows.

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

Кроме того, примеров исходников (причём реально полезных) программ на Турбо-Паскале я скачал ~480 файлов. И ещё ~250 файлов с примерами Паскаль-программ для других компиляторов Паскаля (в основном для МТ+). Учебной литературы по Турбо-Паскалю намного больше, чем по ретро Си (конечно по современным Си и особенно Си++ литературы тоже хватает, но по ретро Си - это лишь Керниган/Ритчи и ещё пара учебных книг из начала 80-тых).

И важное удобство - это совместимость с TP для MSDOS и Windows XP. Что позволяет написать и отладить программу в кодах x86 для MSDOS, а потом странслировать с помощью TP 3.0 для CP/M. При этом можно использовать антикварную версию TP 3.02 для MSDOS из 1985 года (чтобы не соблазняли более поздние расширения языка). Эту версию (и даже версию 1.0) не проблема скачать (а версию 2.0 я не нашёл).

Конечно, прибегнуть к TP 3.0 для MSDOS можно лишь при написании и отладке корректных программ с чисто текстовым выводом и корректным опросом клавиш. Не получится отладить программу, что напрямую лезет в экран РК86 или сканирует клавиатурную матрицу 8*8 через порты ППА. Но такие корректные к DOS программы не лезущие к железу, как, например, текстов редактор, корректный Нортон, свой ассемблер, мелкие программки-конверторы вполне можно писать и отлаживать в TP для MSDOS. С Си так сделать не получится. По крайней мере с Си, что для CP/M из 70-тых и 80-тых (их имеется до десятка).

В корректных программах, если они не чисто текстово-системные (типа трансляторов, конверторов) нужно управление курсором и инверсией знакомест. Во-первых, можно использовать TSR драйвер VT52.SYS для MSDOS. Но даже и без него проблема легко решаема. Относительно позиционирования курсора, в MSDOS-версии TP есть процедура gotoXY. Для 8-ми разрядки не проблема её нацарапать на INLINE-ассемблере:
Code:
procedure gotoxy(x,y: Integer);
begin
  inline($3E/$1B/
        $D7/
        $3E/$59/
        $D7);

    cout(chr(31+y));
    cout(chr(31+x));
end;

procedure cout;
begin
   inline($3A/ch/
        $D7);
end;
или на самом Паскале (что тормознее):
Code:
procedure gotoxy(x,y: Integer);
begin
  if WX1+x <= WX2 then cur_x:=x
     else cur_x:=WX2-WX1+1;
  if WY1+y <= WY2 then cur_y:=y
     else cur_y:=WY2-WY1+1;
    gotoxy(WX1+cur_x-1,WY1+cur_y-1);
{
    cout(#$1B); cout('Y');
    cout(chr(30+WY1+cur_y));
    cout(chr(30+WX1+cur_x));
}
end;
В последнем примере делается оконный вывод (без оконности программы с открывающимися окнами делать неудобно, программист сам должен отслеживать экранные позиции). Задаётся окно координатами углов прямоугольника WX1,WY1 и WX2,WY2 и задав окно (соответствующей функцией WinOpen), все последующие выводы символов попадают в окно.

В вышеприведённых INLINE вставках код $D7 это RST 10. Это быстрый вызов п/п SCOUTA (save regs and CONOUT from reg.A). В моих версиях CP/M активно используются RST (что большое упущение Гарри Килэлла). Они всегда есть, блок RST кидается на адреса $08...$3A по горячему рестарту CP/M (и других DOS). А если мне надо странслировать мою программу для любой CP/M, то я прилинковываю загрузку блока RST в конец этой программы, тогда блок RST закидывается в ОЗУ $0008 при старте программы.

Можно сделать COUT и средствами CP/M:
Code:
procedure cout;
begin
if cur_x<SCR_HSZ then cur_x:=cur_x+1;
bios(3,Integer(ch));
end;
Я ещё не использовал TP 3.02 для MSDOS, лишь скачал. Пока пишу крошечные учебные программки (чтобы вспомнить TP) и транслирую их TP 3.0 для CP/M под TSR-эмулятором, причём и CP/M-эмулятор запускать приходится под DosBox и на крошечной партиции (в 30 мб). В эмуляторе поддерживаются упр.коды консоли VT52 (можно H19, это терминал H19, он в основном совместимый с VT52).

Если не считать ANSI.SYS, то на IBM PC не принято управлять курсором и цветом или инверсией искейп-кодами. В 90-тые годы я пробовал драйвер ANSI.SYS для MSDOS. Перетранслировав под экранные ANSI-коды (и IBM PC коды курсорных клавиш) CP/M-исходник текстового редактора MSDOS версией TP 7.1 в коды x86, получалась возможность эту CP/M-программу с позиционированием курсора и инверсией знакомест использовать в MSDOS (включив загрузку ANSI.SYS в CONFIG.SYS). Но особого смысла в этом нет, т.к в TP 7.1 есть свои функции управления курсором и цветом. Также можно все функции вывода на экран и опроса клавиатуры переписать с использованием INLINE-ассемблера 8086-го (там это намного проще) применяя вызовы функций MSDOS или IBM PC ROM-BIOS. Кроме того, можно встраивать в CP/M-программу свой драйвер клавиатуры и вывода на экран в граф.режимах, совмеcтимый с VT52 (я такой драйвер написал в середине 90-тых для всех графических SVGA режимов).

Можно и не связываться с малоудобным ретро TP 3.0 для MSDOS, а сразу использовать более свежий TP 7.1 с модулем совместимости TP3. Насчёт отладки графических программ есть лишь идея. Хотя, в виду широкого наличия эмуляторов 8-ми разрядок это не необходимо, используя альтернативный BGI для VESA-режимов, можно прямо на PC отлаживать графический вывод, полностью аналогично реальной 8-ми разрядке (только не напрямую работая с экранным ОЗУ, а функциями). Это позволит даже программу работающую по экрану 8-ми разрядки отладить на PC.

А вообще выбор Паскалей для 8-ми разрядки есть, хотя кросс-компиляторов для MSDOS (по крайней мере бесплатно) вообще нет. Кроме TP 3.0 мне удалось скачать (и на почти всех из них даже убедиться, что в них Hello World транслируется), следующие компиляторы: Pascal-МТ+, UCSD-Pascal, HiSoft-Pascal, JRT-Pascal, M-Pascal, Pascal/Z, Pascal-P и даже компилятор PL/0. PL/0 это сильно упрощённый Паскаль. Его придумал Н.Вирт, вероятно, чтобы показать как писать компилятор Паскаля на самом Паскале. Вряд-ли остальные Паскали лучше, чем TP от Borland. К ним стОит присмотреться лишь в случае, если нужен код под КР580, хотя для некоторых Паскалей, (точнее кроме МТ+, JRT и UCSD), практически нет документации.

К тому же неудобство встраивания ассемблерных участков в Турбо-Паскаль теперь существенно сокращается, благодаря тому, что мне удалось найти программу создающая готовые INLINE-вставки прямо из REL-файла. Она написана на самом Турбо-Паскале и исходно была в виде картинок (сканов с бумажного носителя). Я потратил ~10 часов на её редактирование.

 REL --> INLINE конвертор
Code:
{********************************************************************}
{*                                                                  *}
{*  Pascal Link-80 version 3.2                                      *}
{*                                                                  *}
{*  Program to produce an INLINE-statement from an M80 REL-file     *}
{*                                                                  *}
{*  Programmed in Turbo Pascal by J.A.C.G. van der Valk             *}
{*  van Langendonckstraat 2 / 3076 SL Rotterdam                     *}
{*                                                                  *}
{*  Copyright 1986 by J.A.C.G. van der Valk                         *}
{*                                                                  *}
{*  Code is donated to public domain for non-commercial use only!   *}
{*  It is prohibited to sell this code (or any part of it) to 3-rd  *}
{*  parties; to use this code code for any commercial gain or to    *}
{*  distribute any program (in any form), developed by use of the   *}
{*  code in this file, on a commercial basis without prior written  *}
{*  permission of the author.                                       *}
{*                                                                  *}
{********************************************************************}

const
    PrtOn=^P; PrtOff=^N; Bell=^G; LF=^J; CR=^M; UP=^K;

type
    symboltype = string[8];
    fnamtype   = string[20];
    anystring  = string[80];
    hextype    = string[2];
    FileRecord = array [0..127] of byte;
    LoadItem   = record
                    typecode: byte;
                    contents: byte;
                 end;
var
    FileBuffer: FileRecord;

    count,              { counter for lay out of .INC file      }
    offset,             { offset for PC-relative code           }
    DataSize,           { size of Data - segment                }
    ProgSize,           { size of Code - segment                }
    ErrorCnt,           { number of fatal errors detected       }
    EntryCnt,           { number of entry sumbols defined       }
    Maxloc,             { maximum for LC on the heap            }
    LC,                 { Location Counter                      }
    Pbase,              { Base for LC in Code Segment           }
    Dbase,              { Base for LC in Data Segment           }
    Tsize,              { Total Size of Code + Data             }
    BitCnt,             { Number of bit* read from .REL-file    }
    ByteCnt,            { Current Byte-number in .REL-file      }
    ExtCnt,             { Counter for external symbol storage   }
    i:       integer;   { general purpose counter               }
                       
    STOP:   boolean;    { Flag for terminating REL-file reading }
    PrtOut: boolean;    { Flag for shadow-output on printer     }
    symbol: symboltype; { symbol in special link items          }
    s:      string[5];  { string for LC in inl-file             }
    fnam:   string[15]; { file - name of .REL file              }
    WrkRec: LoadItem;   { variable for storing temporary  code  }
    c:      char;       { dummy character                       }
                       
    relfil: file;       { .REL - file from M80 assembler        }
    inlfil,             { file for inline statement             }
    fentry,             { file for table of entry points        }
    f:   text;          { file for listing on LST: or CON:      }
    dsk: byte;          { current disk number                   }

label
    9999;               { label to EXIT the program             }

Procedure ClrEos; var i:byte;
begin
   for i:=14 to 24 do
   begin  gotoxy(1,i); clreol end;
end;

Procedure Error(Errno: integer);
begin
   write(Bell,'Error ',Errno:2,' ');

   case Errno of

   0: begin
        writeln('Unsupported Special Link item');
        writeln('please consult the PL80-manual!');
        end;

   1: begin
        writeln('Code size exeeds available workspace');
        writeln('Split source-code if possible!');
        write('Continue anyhow (Y/N)? :');
        read(kbd,c);if Upcase(c)='Y' then writeln('Y') else writeln('N');
        Stop:=(Upcase(c)<>'Y');
      end;

   2: begin
        writeln('COMMON blocks are not supported');
        writeln('please take notice of the PL80-manual!');
        end;

   3: begin
        writeln('Heap-overflow while linking');
        writeln('Check ORG-statements in source-code!');
        Stop:=true;
        end;

   4: begin
        writeln('Heap-overflow on Chaining External : ',symbol);
        writeln('Split source-code if possible!');
        stop:=true end;

   end; { of case Errno}

        ErrorCnt:=ErrorCnt+1;
   end;

procedure Directory(s:fnamtype);
const
    extend = 12;
    setDMA = 26;
    searchFirst = 17;
    searchNext = 18;
var
    FCB                                 : array[0..32] of char;
    directorynamen                      : array[0..3,0..31] of char;
    drivename                           : char;
    dot,index,numlines,directorycode    : integer;
begin
   for index:=1 to length(s) do s[index]:=Upcase(s[lndex]);
   drivename:=chr(Bdos(25)+65);
   if s[2]=':' then
   begin
     drivename:=Upcase(s[1]);
     s:=copy(s,3,255);
   end;
       
   if Length(s)=0 then s:='*.*';
   writeln('Drive : ',drivename);
   if (s[1]='*') and (s[2]='.') then s:='????????'+copy(s,2,255);
   dot:=Pos('.',s);
   if dot>0 then for index:=dot to 8 do Insert(' ',s,dot);
   if (s[9]='.') and (s[10]='*') then s:=copy(s,1,9)+'???';
   if dot>0 then delete(s,9,1); for index:=Length(s) to 10 do s:=s+' ';
   FCB[0]:=chr(ord(drivename)-64);
   for index:=1 to 11 do FCB[index]:=s[index];
   for index:=12 to 32 do FCB[index]:=chr(0);

   bdos(setDMA,addr(directorynamen));
   directorycode:=bdos(searchfirst,addr(FCB));
   numlines:=0;
   if directorycode=255 then writeln('No files *.REL found');

    while directorycode<255 do
    begin
      if directorynamen[directorycode,extend]=chr(0)
      then
      begin
        if numlines>0 then write(' 1 ');
        write(copy(directorynamen[directorycode],2,8), ^.^);
        for index:=9 to 11 do
        write(chr(127 and ord(directorynamen[directorycode,index])));
        numlines:=(numlines+1)mod 5;
        if numlines=0 then writeln;
      end;
      directorycode:=bdos(searchnext);
   end;
   
   if numlines>0 then writeln;
end;

procedure MemWrite(nn:integer;var WrkRec:LoadItem);
    begin
       if nn<=Maxloc then
       move(WrkRec,mem[nn*sizeof(LoadItem)+HeapPtr],sizeof(LoadItem))
       else Error(3);
    end;

procedure MemRead(nn:integer;var WrkRec:LoadItem);
begin
    if nn<= Maxloc then
    move(mem[nn*sizeof(LoadItem)+HeapPtr],WrkRec,sizeof(LoadItem));
end;

Procedure StoreExtrnl(symbol:symboltype);
var
    WrkRec: LoadItem;
    i: byte;
begin
    if (Tsize+(ExtCnt+1)*9) }= Maxloc then Error(4)
    else
    begin
       for i:=0 to 8 do
       begin
         WrkRec.typecode:=3;
         WrkRec.contents:=ord(symbol[i]);
         MemWrite((Tsize+ExtCnt*9+i),WrkRec);
       end;
       ExtCnt:=ExtCnt+1;
    end;
end;

Procedure GetExtrnl(nn:integer;var symbol:symboltype);
var
    WrkRec: LoadItem;
    i: byte;
begin
    for i:=0 to 8 do
    begin
      MemRead((Tsize+ExtCnt*9+i),WrkRec); symbol[i]:=chr(WrkRec.contents);
    end;
end;

Function Hex(b:byte): hextype;
const
    HexTabl: string[16]='0123456789ABCDEF';
begin
   Hex:=HexTabl[(b shr 4)+1] + HexTabl[(b and $OF)+1];
end;
   
procedure center(str:anystring);
var
   i: integer;
begin
   for i:=1 to (80-length(str)) div 2 do write(' ');
   writeln(str);
end;

procedure conout(c:char);       '
const
    Prt: boolean=false;
begin
    if c=PrtOn then Prt:=true
    else if c=PrtOff then Prt:=false
    else
    begin
       Bios(3,ord(c));
       if Prt and (c<>Bell) then Bios(4,ord(c));
    end;
end;

procedure init;
var
     i: integer;
     dummy: real;
begin
    BitCnt:=-1; ByteCnt:=-1; STOP:=False; ErrorCnti=0;
    EntryCnt:=0; ExtCnt:=0; count:=0;
    LC:=0; Pbase:=0; ProgSize:=-1;
    dummy:=memavail;
    if dummy < 0 then dummy:=dummy+65536.0;
    Maxloc:=Trunc(dummy/sizeof(LoadItem))-1;
    conoutptr:=addr(conout);

    { Hello messages }
    ClrScr; writeln;
    center(' P A S C A L   L I N K  -  80 ');
    writeln;
    center('version: 3.2 / dd:17-07-1986');
    center('Programmed by  J.A.C.G. van der Valk');
    center('van Langendonckstraat 2 / 3076  SL  Rotterdam');
    center('Phone : 010 - 4320625');
    writeln;
    center('(c) 1986 by FalconSoft (tm)');
    writeln(LF);
    writeln('Free work space : ',Maxloc:5,' bytes');
    writeln;
    Directory('*.rel');

    dsk:=Bdos(25);            { get current disk }
    { Input requests }
    writeln('Hit <RETURN> to toggle default drive (A/B)',LF);
   
    write('Name of .REL - file to convert 7 : '); readln(fnam);
    for i:=1 to length(fnam) do fnam[i]:=Upcase(fnam[i]);
   
    i:=Pos('.',fnam);
    if i>0 then delete(fnam,i,255);
   
    if (fnam[2]=':') and (Length(fnam)=2) then
    begin
      dsk:=ord(fnam[1])-65;
      if dsk in [0,1] then Bdos(14,dsk);
      fnam:='  ';
    end;

    {$I-}
    assign(relfil,fnam+'.REL'); reset(relfil);
    while IOresult<>0 do
       begin
         ClrEol;
         if (fnam[2]=':') and (Length(fnam)>0) then
            begin
              dsk:=ord(fnam[1])-65;
              if dsk in[0,1] then Bdos(14,d*k);
              fnam:=copy(fnam,3,255);
            end
         else
         if length(Fnam)=0 then
           begin
             if dsk=1 then dsk:=0 else dsk:=1;
             
             Bdos(14,dsk);
             ClrEos; gotoxy(1,14);
             Directory('*.rel');
             writeln('Hit <RETURN> to toggle default drive (A/B)',LF);
             write('Name of .REL - file to convert ? : ');
             readln(fnam);
             ClrEol;
           end
        else
          begin
            ClrEos; gotoxy(1,14); Directory('*.rel');
            writeln('Hit <RETURN> to toggle default drive (A/B)',LF,LF);
            write(Bell,'File ',chr(dsk+65),':',fnam,'.REL not found',CR,UP);
            write('Name of .REL - file to convert 7 : ');
            ClrEol; readln(fnam);
        end;

        for i:=1 to length(fnam) do fnam[i]:*Upcase(fnam[i]);
        i:=Po$('.',fnam);
        if i>0 then delete(fnam,i,255);
        if (fnam[2]=':') and (Length(fnam)=2) then
        begin
          dsk:=ord(fnam[1])-65;
          if dsk in[0,1] then Bdos(14,dsk);
          fnam:='  ';
        end;
        assign(relfil,fnam+'.REL'); reset(relfil);
     end;
     
     {$I+}
     if fnam[2]=':' then fnam:=copy(fnam,3,255);
     { delete drive specification if still present }
     
     ClrEol;
     Bdos(13);     { reset disk system }
     
     Bdos(14,dsk);  { restore previous default drive }
     write('Send output to LST: device also (Y/N)? :');
     read(kbd,c);
     if Upcase(c)='Y' then writeln('Y',PrtOn) else writeln('N');
     writeln;
   end;
   
function getbit: byte;
var
   mask: byte;
begin
   BitCnt:=(BitCnt+1) mod 8;
   if BitCnt=0
   then ByteCnt:=(ByteCnt+1) mod 128;
   if ByteCnt+BitCnt=0 then begin BlockRead(RelFil,FileBuffer,1); end;
   Mask:=128 shr BitCnt;
   if (FileBuffer[Bytecnt] and mask)=0 then Getbit:=0
               else Getbit:=1;
end;

function RelCode:byte;
begin
    relcode:=(GetBit shl 1) + GetBit;
end;

function CtrlField:integer;
begin
   CtrlField:=(GetBit shl 3) + (GetBit shl 2) + (GetBit shl 1) + GetBit;
end;

function GetByte:byte;
var
    i,B: byte;
begin
    B:=0;
    for i:=7 downto 0
    do B:=B+(GetBit shl i);
    Getbyte:*B; end;
    function GetInteger:integer; begin
    GetInteger:=GetByte + Swap(GetByte);
end;

Procedure GetAfield(var yy,nn:integer);
begin
    yy:=RelCode;
    nn:=GetInteger;
end;

Procedure GetBfield(var symbol:symboltype);
var
    i,zzz:byte;
begin
    zzz:=GetBit*4+GetBit*2+GetBit;
    if zzz=0 then zzz:=8;
    symbol:='';
    for i:=1 to zzz do symbol:=symbol+chr(GetByte); end;

Procedure LoadByte;
begin
    WrkRec.typecode:=0;
    WrkRec.contents:=GetByte;
    MemWrite(LC,WrkRec);
    LC:=LC+1;
end;

Procedure LoadProgRel;
var
    x:integer;
begin
    x:=GetInteger+Pbase;
    WrkRec.typecode:=1;
    WrkRec.contents:=lo(x);
    MemWrite(LC,WrkRec);
    WrkRec.contenta:=hi(x);
    MemWrite(LC+1,WrkRec);
    LC:=LC+2;
end;

Procedure LoadDataRel;
var
    x:integer;
begin
    x:=GetInteger+Dbase;
    WrkRec.typecode:=1;
    WrkRec,contents:=lo(x);
    MemWrite(LC,WrkRec);
    WrkRec.contents:=hi(x);
    MemWrite(LC+1,WrkRec);
    LC:=LC+2;
end;

Procedure LoadCommRel;
var
    x:integer;
begin
    x:=GetInteqer;
    LC:=LC+2;
end;

Procedure SpecialLink;
var
   x,yy,nn,nextloc,nextcode: integer;
begin
   Case CtrlField of
   0: begin
      GetBfield(symbol);
      writeln('Entry symbol   --> ',symbol);
   end;
   
   1: begin
        GetBfield(symbol);
        writeln('Select COMMON block      --> ',symbol);
        Error(2);
      end;
     
   2: begin
        GetBfield(symbol);
        writeln('Program Name   -> ',symbol);
      end;
   
   3: begin
        GetBfield(symbol);
        writeln('Request iibrary search   --> ',symbol);
        Error(0);
      end;
   
   4: begin
        GetBfield(symbol);
        writeln('Extension Link item      --> ',symbol);
        Error(0);stop:=true;
      end;
   
   5: begin
       GetAfield(yy,nn); GetBfield(symbol);
       writeln('Define COMMON size       -> ',symbol);
       writeln('$',Hex(yy),'  $',Hex(hi(nn)),Hex(lo(nn)));
       Error(0);
   end;
   
   6: begin
        GetAfield(yy,nn); GetBfield(symbol);
        writeln('Chain External --> ',symbol);
       
        case yy of
          1: nn:=nn+Pbase;
          2: nn:=nn+Dbase;
          3: begin nn:=nn+Tsize; Error(2); end;
        end; { of case }
   
        MemRead(nn,WrkRec);
        writeln('loc = $',Hex(hi(nn)),Hex(lo(nn)));
        nextloc:=nn; nextcode:=yy;
        if nn+yy=0 then
        writeln('WARNING: Chain is empty, inspect source code!');
       
        while nextcode+nextloc>0 do
          begin
             nextloc:=WrkRec.contents;
             nextcode:=WrkRec.typecode;
             WrkRec.contents:=lo(ExtCnt); WrkRec.typecode:=2;
             MemWrite(nn,WrkRec);
             MemRead(nn+i,WrkRec);
             nextloc:=nextloc+swap(WrkRec.contents); if nextcode+nextloc>0 then
             writeln('loc = $',hex(hi(nextloc)),hex(lo(nextloc)));
             WrkRec.typecode:=2;
             WrkRec.contents:=hi(ExtCnt);
             MemWrite(nn+1,WrkRec); nn:=nextloc;
             MemRead(nn,WrkRec);
          end;
       
        StoreExtrnl(symbol);
      end;
   
   7: begin
        GetAfield(yy,nn);GetBfield(symbol);
        writeln('Define Entry point       --> ',symbol);
        writeln('$',Hex(yy),'  $',Hex(hi(nn)),Hex(lo(nn)));
        case yy of
           1:nn:=nn+Pbase;
           2:nn:=nn+dbase;
           3:begin
               ErrorCnt:=ErrorCnt+1;
               writeln('Error: entry in common block');
             end;
        end; { of case }
       
        if EntryCnt=0 then
           begin
             assign(fentry,fnam+'.ENT'); rewrite(fentry);
             writeln(fentry,'Table of Entry symbols ');
             writeln(fentry,'corresponding to ',fnam,'.INL');
             writeln(fentry);
           end;
       
        write(fentry,symbol);
       
        for x:=1 to 8-length(symbol) do write(fentry,' ');
        writeln(fentry,' = $',hex(hi(nn)),Hex(lo(nn)));
        EntryCnt:= EntryCnt+1;
      end;
       
   8: begin
        getAfield(yy,nn);
        write('External - offset        -> ');
        writeln('$',Hex(yy),'  $',Hex(hi(nn)),Hex(lo(nn)));
        Error(0);
      end;
     
   9: begin
        getAfield(yy,nn);
        write('External + offset        --> ');
        writeln('$',Hex(yy),'  $',Hex(hi(nn)),Hex(lo(nn)));
        Error(0);
      end;
   
   1O: begin
         getAfield(yy,nn);
         write('Define Size of DATA area  --> ');
         writeln('$',Hex(hi(nn)),Hex(lo(nn)));
         DataSize:=nn;
       end;
     
   11: begin
         GetAfield(yy,nn);
         write('Set Loading LC to ');
         case yy of
            1: write('CSEG');
            2: write('DSEG');
            3: write('COMM');
            end; { of case }
         
         writeln('    --> $',Hex(hi(nn)),Hex(lo(nn)));
         case yy of
            1: LC:=Pbase+nn;
            2: LC:=Dbase+nn;
            3: LC:=Tsize+nn;
         end; { of case }
       end;
   
   12: begin
         getAfield(yy,nn);
         write('Chain Address    --> ');
         writeln('$',Hex(yy),'  $',Hex(hi(nn)),Hex(lo(nn)));
         nextloc:=nn; nextcode:=yy;
         while nextcode+nextloc>0 do
           begin
             nextloc:=WrkRec.contents; WrkRec.contents:=LC;
             nextcode:=WrkRec.typecode;
             MemWrite(nn,WrkRec); MemRead(nn+1,WrkRec);
             nextloc:=nextloc+swap(WrkRec.contents);
             writeln('loc = $',hex(hi(nextloc)),hex(lo(nextloc)));
             WrkRec.contents:=LC;
             MemWrite(nn+1,WrkRec);
             nn:=nextloc; MemRead(nn,WrkRec);
           end;
       end;
             
   13: begin
         getAfield(yy,ProgSize);
         writeln('Define PROGRAM Size      --> $',
         Hex(hi(ProgSize)),Hex(lo(ProgSize)));
         Dbase:=ProgSize+3;            { create space for JP ENDofDSEG }
         Tsize:=ProgSize+DataSize;
         if datasize>0 then tsize:=tsize+3;
         if Tsize}Maxloc then Error(1)
         else
           begin
             WrkRec.typecode:=0; WrkRec.contents:=0;
             for x:=0 to Tsize-1 do MemWrite(x,WrkRec);
             { Block-Data space (DS) initialized to null-bytes }
         end;
      end;
     
   14: begin
         getAfield(yy,nn); writeln('END of PROGRAM');
         Bitcnt:=-1;   { forces to next byte boundary }
       end;
           
   15: begin
         Stop:=true;
         writeln('END of FILE');
       end;
   
     end; { of case }

end;

begin                           { main program }
    init;
    While not STOP do
    begin
       if Getbit=0 then loadbyte
       else
       begin
         case relcode of
           0:SpecialLink;
           1:LoadProgRel;
           2:LoadDataRel;
           3:LoadCommRel;
         end; { of case }
       end;
    end;

    writeln(LF,LF,'diagnostics for linkage of file ',fnam,'.REL',LF);
    writeln('Free workspace available : ',Maxloc:5,' bytes');
    if Tsize<Maxloc then
    writeln('Total workspace used     : ',(Tsize+ExtCnt*9):5,' bytes')
    else writeln('Code size exeeds work space');
    writeln('Total code size          : ',Tsize:5,' bytes');
    writeln('Code segment size        : ',Progsize:5,' bytes');
    writeln('Data segment size        : ',Datasize:5,' bytes');
    if Tsize < Maxloc then
    begin
       if ExtCnt > 0 then
       writeln('number of externals      : ',ExtCnt:5)
       else writeln('no externals used');
       if EntryCnt > 0 then
       writeln('number of entry-points   : ',EntryCnt:5)
       else writeln('no entry points defined');
    end;

    LC:=0;
    if EntryCnt>0 then close(fentry);

    if ErrorCnt>0 then
    begin
       writeln(f);
       writeln(ErrorCnt:3,' fatal error(s) detected',LF);
       write('make inline-file anyway (Y/N)? '); read(kbd,c);
       if Upcase(c)<>'Y' then
          begin
            writeln('N');
            if EntryCnt>0 then erase(fentry);
            goto 9999;
          end
       else writeln('Y');
    end
     
    else writeln('no fatal errors detected');
    writeln(PrtOff);
    writeln('creating ',fnam,'.INL...');
    write('Bytes written : ',#27,'.0');
    assign(inlfil,fnam+'.INL');
    rewrite(inlfil);
    write(inlfil,' INLINE({00000} ');
   
    while LC < Tsize do
    begin
      MemRead(LC,WrkRec);
     
      case WrkRec.typecode of
        0: begin
             write(inlfil,'$',Hex(WrkRec.contents));
             LC:=LC+1;
           end;
       
        1: begin
             offset:=WrkRec.contents;
             MemRead(LC+1,WrkRec);
             offset:=offset+swap(WrkRec.contents)-LC;
             if offset>0 then write(inlfil,'*+',offset)
                         else write(inlfil,'*',offset);
             LC:=LC+2;
           end;
       
        2: begin
             ExtCnt:=WrkRec.contents; MemRead(LC+1,WrkRec);
             ExtCnt:=ExtCnt+swap(WrkRec.contents);
             GetExtrnl(ExtCnt,symbol); write(inlfil,symbol);
             LC:=LC+2;
           end;
        else
           begin
           write('Internal error, program aborted'); close(inlfil);erase(inlfil);
           goto 9999;
        end;
       
      end; { of case }
   
       if LC < Tsize then write(inlfil,'/')
          else writeln(inlfil,');');
       if (LC=ProgSize) and (Datasize>0) then
         begin
            writeln(inlfil,'$C3/*+',Tsize-LC-1,'/');
            LC:=LC+3;
            writeln(inlfil,'        { start of DATA segment }');
            Str(LC,s);
             for i:=length(s) to 4 do s:='0'+s;
            write(inlfil,'        {',s,'} ');
            count:=0;
         end;
       count:=(count+1) mod 10;
       if count=0 then
         begin
           writeln(inlfil);
           Str(LC,s);for i:=length(s) to 4
           do s:='0'+s; write(inlfil,'        {',s,'} ');
         end;
       
       write(LC:5,^H,^H,^H,^H,^H);
       end;
       close(inlfil);

9999:
    writeln(#27,'.2');
    close(relfil);
end.

Что касается компиляторов Си. Я имел дело только с древними Си для КР580 из начала 80-тых, - BDS и AZTEC. И по-моему они уступают Турбо-Паскалю по качеству кода. Недавно я скачал и более свежие компиляторы HiTech C и SDCC (и ещё до десятка менее солидных). Естественно предположить, что более поздние разработки, тем более в виде кросс-программ с оптимизацией могут давать плотность кода не уступающую Турбо-Паскалю (но сам я это не видел, это лишь логичное предположение исходя из большей популярности этих компиляторов). Документация на них также хорошая. Может и ещё какие-то прогрессивные компиляторы есть. Было бы интересано, если бы программирующие на этих Си сообщили об их эффективности. Я сам вряд-ли займусь Си для 8-ми разрядки.

PL/M намного эффективнее, но разработка на нём длится на порядок (а то и два) дольше, чем на Турбо-Паскале и в разы дольше, чем на Си. И освоение PL/M, несмотря на его предельную простоту, отнимает больше времени, чем Си или Паскаля. Да и практически нет учебников и (для PLMX) совсем нет примеров чужих исходников. А если уж необходима эффективность, то похоже, лучше начать с PLI от Digital Research. И для процессора КР580 PL/M и PL/I остаются вполне разумным вариантом.


Last edited by barsik on 07 Nov 2019 04:09, edited 1 time in total.



06 Nov 2019 23:40
Profile
Doomed

Joined: 01 Oct 2007 10:30
Posts: 665
Location: Ukraine
Reply with quote
barsik, я не знаю, как вы относитесь к Си. Но все выше сказанное можно переложить на HI-TECH Z80 C Compiler. Возможностей и гибкости у него будет побольше Paskal-я.

И да. Есть возможность у их него ассемблера jp заменять на jr. Где это возможно. Оптимизация кода так сказать.

_________________
Эмулятор OrionEXT:
http://www.orion-ext.narod.ru


07 Nov 2019 06:27
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 583
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post 
Alekcandr wrote:
не знаю, как вы относитесь к Си
Нормально. Он мне даже нравится и писать на нём мне пришлось заметно больше, чем на Паскале. Сложные многоэтажные конструкции понятнее на Си и с указателями в Паскале хуже (они используются в TP лишь для управления динамической памятью). В CP/M я долго решал, что лучше BDS или AZTEC, в итоге использовал и тот и другой (а в литературе больше упоминаний про BDS и он удобнее для ассемблера). В MSDOS только Турбо-СИ (с Visual-C не смог разобраться, точнее не особо и хотел, т.к задач под него не было).
Alekcandr wrote:
Но все выше сказанное можно переложить на HI-TECH Z80 C Compiler. Возможностей и гибкости у него будет побольше Paskal-я
У любого Си возможностей побольше, чем у Паскаля. Ещё даже CP/M-версия HiTeсh 3.09 выпущенная в октябре 1989 (30 лет) производит хорошее впечатление. Это намного позднее, чем конец 70-тых, когда появились первые версии Паскалей для 8080 (а видимо, чем компилятор свежее, тем лучше). И документация производит хорошее впечатление. А уж кросс-компилятор наверняка ещё лучше (не знаю, когда его бросили сопровождать и дорабатывать, но явно позднее). На форумах только и встречаются пользователи его, SDCC, ZDK88 и ещё IAR (но его трудно скачать и он платный). А ретро CP/M-версиями Си для КР580 из 1981 уже, похоже, никто не пользуется.
Alekcandr wrote:
И да. Есть возможность у ихнего ассемблера JP заменять на JR. Где это возможно. Оптимизация кода так сказать.
Интересно, надо будет взглянуть, вдруг этот ассемблер можно использовать отдельно для трансляции программ вручную написанных на ассемблере.


Last edited by barsik on 07 Nov 2019 07:24, edited 1 time in total.



07 Nov 2019 06:57
Profile
Doomed

Joined: 01 Oct 2007 10:30
Posts: 665
Location: Ukraine
Reply with quote
Post Re:
barsik wrote:
Интересно, надо будет взглянуть, вдруг этот ассемблер можно использовать отдельно для трансляции программ вручную написанных на ассемблере.
Это ассемблер обладает всеми необходимыми атрибутами. Но в силу внутреннего стандарта фирмы HiTeсh может вам показаться в чем то не обычным по равнению с классикой от меломягких. Контора HiTeсh поддерживала не вероятно количество процессоров 80г. Последний релиз для Z80 был сделан на PC 7.80PL2 2001г.

Но как бывает в жизни, все не вечно. HiTeсh поглотил MicroChip (или наоборот :mrgreen: ). Этот MicroChip относительно недавно сожрал Atmel :mrgreen:

_________________
Эмулятор OrionEXT:
http://www.orion-ext.narod.ru


07 Nov 2019 07:26
Profile
Admin
User avatar

Joined: 08 Jan 2003 23:22
Posts: 23468
Location: Silicon Valley
Reply with quote
Вытаскиваю топик из небытия, освободив от лишних "лирических" отступлений...

_________________
https://mastodon.social/@Shaos :dj:
https://www.youtube.com/@Shaos1973


04 Feb 2023 20:58
Profile WWW
Maniac

Joined: 21 Jan 2003 04:08
Posts: 242
Reply with quote
Еще в прошлом году провел небольшое мини-исследование эффективности разных ЯВУ при генерации кода для i8080. Для примера была выбрана следующая задача на РК-86: на основании информации в ПЗУ экран заполняется "случайными" клетками и прогоняется 10 итераций по правилам игры "жизнь". Один и тот же алгоритм был реализован на ассемблере, Си (z88dk), с8080 (компилятор Си Алексея Морозова), PL/M, Millfork. Сильно оптимизировать алгоритм не старался, в конце для каждого языка пробовал сделать минимальную оптимизацию - разные типы для переменных в критических участках, альтернативные записи выражений и т.п. и выбирал лучший по быстродействию вариант.

Результаты не претендуют на достоверность во всех случаях, но иллюстрируют общую тенденцию. Прогонял еще некоторые тесты - результаты похожие.


Attachments:
File comment: Тесты, исправленная версия
8080_lang_tests_fixed.zip [24.09 KiB]
Downloaded 27 times
File comment: Размер кода
size.png
size.png [ 5.8 KiB | Viewed 681 times ]
File comment: Скорость выполнения
speed.png
speed.png [ 6.98 KiB | Viewed 681 times ]

_________________
Эмулятор Emu80


Last edited by Pyk on 11 Jun 2024 13:18, edited 2 times in total.

11 Jun 2024 09:38
Profile ICQ WWW
Admin
User avatar

Joined: 08 Jan 2003 23:22
Posts: 23468
Location: Silicon Valley
Reply with quote
А кто такой Millfork?

Надо на моём языке Robby попробовать ;)

Это который бывший RW1 из состава бывшего RW1P2 (теперь nedoPC SDK)…

_________________
https://mastodon.social/@Shaos :dj:
https://www.youtube.com/@Shaos1973


11 Jun 2024 12:47
Profile WWW
Maniac

Joined: 21 Jan 2003 04:08
Posts: 242
Reply with quote
Shaos wrote:
А кто такой Millfork?

https://karols.github.io/millfork/

_________________
Эмулятор Emu80


11 Jun 2024 12:55
Profile ICQ WWW
Display posts from previous:  Sort by  
Reply to topic   [ 31 posts ]  Go to page Previous  1, 2, 3  Next

Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.