nedoPC.org

Electronics hobbyists community established in 2002
Atom Feed | View unanswered posts | View active topics It is currently 28 Mar 2024 07:20



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

Joined: 19 Feb 2017 03:46
Posts: 584
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
.
Не нашёл раздела, специально предназначенного для обсуждения программирования для ретро ЭВМ, потому создал эту тему в разделе РК. Хотя всё, что не привязано к железу (адресам экрана, портов, ячейкам, входам в ПЗУ) справедливо и для других ЭВМ с процессором КР580.

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

А ещё в разделах по каждому ретро компьютеру не хватает раздела "Разное", где удобно задавать и обсуждать мелкие вопросы, ради которых нет смысла создавать целую тему.

Несколько дней назад решил освоить программирование на Паскале МТ+ для РК86.

Почему именно для РК86? Потому что программа странслированная на ЯВУ работает примерно в 5-7 медленнее, чем программа написанная на ассемблере. А т.к вывод на экран в чисто текстовой машине происходит в ~50 раз быстрее, чем в графической, то это даёт основание предполагать, что для РК скоростей странслированной с ЯВУ программы хватит, причём не только для системных программ, но и для несильно динамичных игр. Особенно с учётом, что РК за счёт простейшей установки второго кварцевого генератора на 531ЛН1 (как указано в ж.Радио 01.1991) турбируется в 1.5-2 раза, а при замене CPU на 8085 или даже Z80, - в разы больше.

Почему именно на Паскале, а не на Си или Коболе? Потому что Паскаль мне приятнее, симпатичнее, чем Си, а при программировании разницы практически нет (кто к чему привык, то ему и лучше), хотя по удобочитаемости исходников, - Си лучше, т.к код проще и компактнее. А почему именно МТ+ а не другие Паскали можно почитать здесь.

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

Для РК86 уже разработаны аж целых две схемы, позволяющие загрузку фонтов. К сожалению, они непопулярны, программно неподдержаны и печатных плат нет. А далеко не каждый в состоянии спаять и настроить сложную конструкцию на картонке проводом МГТФ. Но в этом и нет особого смысла, т.к практически тот же результат получается за 3 секунды работы путём припайки одного куска проволоки. Так на моём РК сделано с начала 90-тых. Т.к в ПЗУ РФ2 умещаются два фонта, то получаются основной (стандартный) и альтернативный фонты, переключаемые адресом A10 на РФ2.

Из-за того что, несмотря на статью о фонтах в РК в ж.РАДИО, ни у кого не хватило здравого смысла опубликовать конкретную доработку (а точнее просто задать стандарт как и какими битами программно переключать фонт ПЗУ), то для РК так и не появилось игр с красивой графикой и системных программ с окнами и нормальными графическими рамками. Автор статьи посчитал, что альтернативный фонт нужен лишь для печати на принтере в КОИ-8, тогда как гораздо полезнее он был бы, если бы содержал графику полезную при написании игр. Тем более, что в те годы из принтеров был доступен только АЦПУ "Консул" имеющий лишь КОИ-7.

Только я разумно использовал альтернативный фонт, чтобы поиметь нормальную графику и окна в системных программах РК86. Посмотрите насколько нормальная графика (получаемая за счёт расхода в один кусок проволоки) выглядит лучше, чем убогая матрично-символьная графика с размером пикселей в 4*3 точки:
Attachment:
1NortonRKstartoval.1524937326.png
1NortonRKstartoval.1524937326.png [ 5.17 KiB | Viewed 4499 times ]

Attachment:
1NortonRK-HELP-2.png
1NortonRK-HELP-2.png [ 5.78 KiB | Viewed 4499 times ]

Attachment:
1NortonRK-HELP2-3.png
1NortonRK-HELP2-3.png [ 6.19 KiB | Viewed 4499 times ]

Attachment:
1NortonRK-VyborDiska-4.png
1NortonRK-VyborDiska-4.png [ 5.29 KiB | Viewed 4499 times ]

Attachment:
1FONTVIEW-5.png
1FONTVIEW-5.png [ 2.76 KiB | Viewed 4499 times ]

Attachment:
1FONTVIEW2-6.png
1FONTVIEW2-6.png [ 3.02 KiB | Viewed 4499 times ]

Attachment:
1FONT-7.png
1FONT-7.png [ 3.42 KiB | Viewed 4499 times ]


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

Я об этом писал ещё пол-года назад, но реакции - ноль. Ясно, что это потому что для РК осталось всего 2.5 программиста любителя, к тому же уже погрязших в ассемблере. Потому ничего другого не остаётся как самому попробовать.

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

Интерфейс с стандартными подпрограммами РК делается совсем просто за счёт инлайн-вставок на ассемблере. Например, вот так:

 Интерфейс из МТ+ с подпрограммами ПЗУ РК86
Code:

function keypressed: boolean;
begin
   keypressed:=false;
   inline( "CALL/$F812/
           "ORA A/
           "RZ );
  keypressed:=true;
end;

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

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 print(str: string);          ; вывод строки Паскаля
begin
    INLINE( "LXI H/str/
            "MOV B,M/
            "INX H/
            "MOV C,M/
            "CALL/$F809/
            "INX H/
            "DCR B/
            "JNZ/*-6 );
end;

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

procedure mssg(asciiz: ukchar);          ; вывод строки с завершающим нулём
begin
   INLINE( "LHLD/asciiz/
           "CALL/$F818 );
end;

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

function chsum(begad: word; endad: word): integer;
var
result: integer;
begin
   INLINE ( "LHLD/ENDAD/
            "XCHG/
            "LHLD/BEGAD/
            "CALL/$F82A/
            "MOV L,C/
            "MOV H,B/
            "SHLD/result );
   chsum := result;
end;

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

procedure JMP(ADR: word);
begin
   INLINE( "LHLD/ADR/
           "PCHL );
end;
 
{------------------------------------------------}

procedure gotoxy(X,Y: byte);
begin
  INLINE( "MVI C/$1B/
          "CALL/mcout/
          "MVI C/$59/
          "CALL/mcout/
          "LDA/Y/
          "ADI/32/
          "MOV C,A/
          "CALL/mcout/
          "LDA/X/
          "ADI/32/
          "MOV C,A/
          "CALL/mcout );
end;

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

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

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

procedure wordhex(i: integer);
begin
    hex(hi(i)); hex(lo(i));
end;

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

procedure whexbl(i: integer);
begin
    wordhex(i); space;
end;

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

procedure cls;
begin
  INLINE( "MVI C/$1F/
          "CALL/$F809 );
end;

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

procedure spaces(n: byte);
begin
  while n>0 do
    begin
      space; n:=n-1;
    end;
end;

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

function peek(adr: integer): byte;
var result: integer;
begin
  inline( "LHLD/adr/
          "MOV A,M/
          "STA/ result );
    peek := result;
end;

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

procedure poke(adr: integer; byt: byte);
begin
  inline( "LDA/byt/
          "LHLD/adr/
          "MOV M,A );
end;

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

function dpeek(adr: integer): word;
var result: word;
begin
    inline( "LHLD/adr/
            "MOV E,M/
            "INX H/
            "MOV D,M/
            "XCHG/
            "SHLD/result );
    dpeek := result;
end;

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

procedure dpoke(adr: integer; ww: integer);
begin
  inline( "LHLD/ww/
          "XCHG/
          "LHLD/adr/
          "MOV M,E/
          "INX H/
          "MOV M,D );
end;

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

procedure intdes(ii: integer);          ; вывод integer в десятичном виде
var     ost: integer;
        bb: byte;
        flag: boolean;
begin
   if tstbit(ii,15) then
      begin cout('-');
       if ii=$8000 then print('32768')
         else
          intdes($8000-(ii & $7FFF));
      end
   else
     
    if hi(ii)=0 then des(ii)
      else
       begin
         bb:= ii div 1000; ost:=ii mod 1000;
         if bb<>0 then flag:=true else flag:=false;
         if flag then des(bb);
         bb:= ost div 100; ost:= ost mod 100;

         if flag then onedes(bb)
           else
             if bb<>0 then
                begin onedes(bb); flag:=true;
         end;

        bb:= ost div 10;
        if flag then onedes(bb)
           else
             if bb<>0 then
        onedes(bb);
             
        des(ost mod 10);
    end;
   end;



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

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


Last edited by barsik on 09 Nov 2018 19:49, edited 4 times in total.



28 Apr 2018 10:36
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
barsik wrote:
Кроме того, при 8-ми разрядке для игр чисто графический режим невыгоден из-за нехватки скорости, да и из-за логики работы игр, в которых надо перемещать не точки, а большие спрайты.

Вот тут можно и возразить немного... Одна из первых графических игр на "Специалисте", которую
я расковыривал, чтобы чему-то научиться - была ZOO из журнала МК.

Я просто что-то правил в кодах и смотрел - а что из этого получится?
Ну и обнулил я из интересу циклы каких-то счетчиков, а это оказались задержки после вывода
спрайтов на экран...
Так персонажи так носились по экрану :o (а там была анимированная заставка), что от них как
будто оставался след, как от пуль в фильме "Матрица"... :wink:

И ощутил я, что 8-битник на 2 МГц - это весьма не хило! 8)
Тут всё еще зависит от структуры графического экрана, а в "Специлисте" он построен удобнее,
нежели в ZX-Spectrum, поэтому вывод на него быстрее, хотя и растр больше.


Что касается ЯВУ - мне очень захотелось ЯВУ компилирующего типа, когда я начал делать на
"Специалисте" рассчеты - интегрирование систем уравнений методом Рунге-Кутты с выводом
результатов на графический экран...
Так оказалось, что больше всего тормозит не вывод графики, а сам интерпретатор ВАСИК.
Ну и поскольку никаких Паскалей и С++ в от момент и в помине не было, мы другом нашли
по объявлениям компилятор ВАСИК у одной фирмы в Воронеже, правда он был под CP/M, но
мне казалось - достаточно перенаправить вызовы функций CP/M в аналоги в системном мониторе,
и всё будет работать...

Этот "фокус" не я придумал - есть под Микрошей отладчик DDT - он запускался с адреса 100Н,
что тогда было странно, т.к. в основном программы грузились с 0000Н.
Я поковырял DDT и выяснил, что ниже 100Н, он формирует при старте некую структуру, где есть
переходы на системные подпрограммы ПЗУ.
Интересно, что этот отладчик DDT понимал коды Z80, хотя и был в софте для Микроши. Ну и я понял
тогда, что его так портировали из-под CP/M.

Однако, с компилятором ВАСИК, который мы купили, этот фокус не прошел, скорее всего потому,
что CP/M я тогда практически не знал, и информацию взять было негде. :osad:

Честно говоря на сегодняшний день мне более интересен компилятор С++ под "Специалист".
Здесь у нас на форуме он есть, смотрел я его, но уж больно он убог и более напоминает
какой-то учебный проект... :-?

_________________
iLavr


28 Apr 2018 15:07
Profile
Senior
User avatar

Joined: 11 Jun 2012 07:30
Posts: 128
Reply with quote
begoon писал когда-то в своем блоге:
Quote:
Знаю, что С - это начало всех начал, и при правильном использовании можно писать очень близко по эффективности к ассемблеру. Но, все же есть еще системы, где компилятору С сложно развернуться. Например, захотел я подыскать компилятор С для Intel 8080, чтобы замутить небанальную программу для Радио-86РК. Из реально собираемого я нашел только пару наследников знаменитого Small-C – smallc-85 и smallc-scc3.

Увы, для простейшей программы типа:
Code:
main() {
  static char a;
  for (a = 1; a < 10; ++a) {
     ++a;
  }
}

Генерируется адъ типа:
Code:
;main() {
main:
;  static char a;
    dseg
?2: ds  1
    cseg
;  for (a = 1; a < 10; ++a) {
    lxi h,?2
    push    h
    lxi h,1
    pop d
    call    ?pchar
?3:
    lxi h,?2
    call    ?gchar
    push    h
    lxi h,10
    pop d
    call    ?lt
    mov a,h
    ora l
    jnz ?5
    jmp ?6
?4:
    lxi h,?2
    push    h
    call    ?gchar
    inx h
    pop d
    call    ?pchar
    jmp ?3
?5:
;     ++a;
    lxi h,?2
    push    h
    call    ?gchar
    inx h
    pop d
    call    ?pchar
;  }
    jmp ?4
?6:
;}
?1:
    ret

Понятно, что много вопросов к компилятору, но в целом, Intel 8080 не очень удобен для компилятора С: деления/умножения нет, косвенной адресации через стек тоже нет и т.д.


Last edited by alexcp on 28 Apr 2018 19:27, edited 1 time in total.



28 Apr 2018 18:28
Profile WWW
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Мне кажется, исторически просто так сложилось, что для 8080 нет эффективного С++ компилятора.
На мой взгляд, ассемблеры с развитыми макросредствами предопределили его невостребованность.
Раз уж компиляторы с ВАСИКа всё же существовали.

_________________
iLavr


28 Apr 2018 18:42
Profile
Senior
User avatar

Joined: 11 Jun 2012 07:30
Posts: 128
Reply with quote
Насколько я понимаю, создавать целиком новый компилятор под конкретный процессор в наши дни необязательно, достаточно написать backend для gcc или llvm и получить набор эффективных компиляторов для разных языков высокого уровня. Комментарий begoon'а при этом остается актуальным - к примеру, отсутствие индексной адресации стека делает неудобным обращение к аргументам функций/процедур и т.п., что делает более громоздким и медленным полученный в результате код. Тем не менее, есть, например, вот такой проект: LLVM backend for Z80.


28 Apr 2018 19:25
Profile WWW
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
alexcp wrote:
Комментарий begoon'а при этом остается актуальным - к примеру, отсутствие индексной адресации стека делает неудобным обращение к аргументам функций/процедур и т.п., что делает более громоздким и медленным полученный в результате код.

А вы вспомните, на чем начинали писать С - мне кажется, ситуация была нисколько не лучше.
С-компиляторы (и ВАСИК-компиляторы) есть для семейства PIC с весьма ограниченными ресурсами
и набором команд.

Вероятно, всё-таки хороший компилятор С для 8080 следует написать отдельно, учитывая его специфику.

_________________
iLavr


28 Apr 2018 20:09
Profile
Senior
User avatar

Joined: 11 Jun 2012 07:30
Posts: 128
Reply with quote
Lavr wrote:
А вы вспомните, на чем начинали писать С - мне кажется, ситуация была нисколько не лучше.

С начинали писать на PDP, архитектура которой, несмотря на ограниченные ресурсы, существенно богаче архитектуры ранних микропроцессоров. В частности, у PDP много режимов адресации, в том числе стека. То, что все это богатство невозможно было уложить в прокрустово ложе ранних микропроцессоров, и делает эти микропроцессоры неудобными для C/C++ компиляторов (а также для Unix-подобных операционных систем). Offtopic: со временем DEC сделал-таки PDP в виде микропроцессора.

Lavr wrote:
С-компиляторы (и ВАСИК-компиляторы) есть для семейства PIC с весьма ограниченными ресурсами
и набором команд.

Вероятно, всё-таки хороший компилятор С для 8080 следует написать отдельно, учитывая его специфику.
Возможно - спорить не буду. С одной стороны, действительно, специфика архитектура младших PIC (PIC10, PIC12) - например, 8-уровневый аппаратный стек - такова, что сделать GCC backend для нее затруднительно, при этом компиляторы С для PIC давно и успешно существуют. С другой стороны, 8080/8085/Z80 не настолько экзотичен, backend для LLVM вроде бы несколько проще, чем для GCC, его уже даже кто-то пытается написать, а готовая полноценная поддержка C++ чего-нибудь да стоит.

Кстати, пока писал пост, сложил два факта. Во-первых, компилятор языка B, предшественника C, выдавал не двоичный машинный код, а "шитый код", исполняемый на стековой машине. Во-вторых, стековая машина для исполнения "шитого кода" описана в том самом посте в блоге begoon'а, цитату из которого я привел выше.

Ну и, конечно, топикстартер уже пишет на Паскале для РК, пока мы обсуждаем высокие материи :wink: Существует, между прочим, Турбо Паскаль для CP/M. У меня где-то лежит Zeta SBC, на которой он даже работает.


28 Apr 2018 21:07
Profile WWW
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
alexcp wrote:
Lavr wrote:
А вы вспомните, на чем начинали писать С - мне кажется, ситуация была нисколько не лучше.
С начинали писать на PDP, архитектура которой, несмотря на ограниченные ресурсы, существенно богаче архитектуры ранних микропроцессоров.

Мне думается, это не совсем принципиально. Мы в общем-то в топике говорим о компиляторах ЯВУ
для платформ с ограниченными ресурсами
.

То, что мне сейчас интересен именно компилятор С, - так это потому, что я зело покопался в его недрах,
когда занимался инжекцией в код готовой программы.
И мне очень понравилось в коде, что делает компилятор С.

Но, скажем, та же PDP-8 - ЭВМ с весьма убогими ресурсами даже рядом с 8080, но эффективные
компиляторы с ЯВУ для нее существовали.

Когда я писал на ВАСИК для "Специалиста" мне комплятор С и нафиг был не нужен, мне надо
было ускорить собственные программы, а для этого нужен был компилятор ВАСИК!

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

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



P.S. И есть вопрос по поводу компилятора более значимый в данный момент - в то время компилятор
был нужен на той же платформе, на которой писали сам код. Сейчас - это скорее всего - кросс-компилятор с ЯВУ.

_________________
iLavr


29 Apr 2018 05:49
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
rw6hrm wrote:
...к сям отношусь более чем прохладно, но ссылки под компиляторы есть.

А на компилятор ВАСИК-а для 8080, случаем, нет ссылочки? :wink:

Интересно было бы разочек попробовать то, что я не сподобился сделать в года давно минувшие... 8)

_________________
iLavr


29 Apr 2018 10:59
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 584
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Post .
По поводу вопроса о том как узнавать адреса меток в INLINE-ассемблере. Похоже, что встроенный в Паскаль МТ+ ассемблер не позволяет без ухищрений встраивать программу содержащую абсолютные адреса переходов.

Для процессора Z80 это не проблема, т.к там есть JR-команды. На КР580 тоже можно написать перемещаемую программу, для чего достаточно иметь внешнюю подпрограмму возвращающую в регистре HL адрес с которого его вызвали (так устроены SYS-файлы в RK-DOS Е.Седова). Такой метод в данном случае тоже годится, но невыгоден, т.к вместо одной маш.команды перехода тратится более десятка маш.команд (из-за PUSH-POP-ов для HL,DE).

В документации указано, что в ассемблерных инлайн-вставках для задания текущего адреса работает символ '*', (аналогично символу '$' в ассемблерах), но на практике это не работает. Таким образом в МТ+ на ассемблере можно реализовывать только линейные участки, что резко снижает ценность такого встроенного ассемблера.

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

Далее транслируем обычным ассемблером этот же фрагмент с полученного адреса и получив дамп, оформляем его дампом, как положено для INLINE. Текстовый дамп из кодов делается простенькой программкой на бейсике полученной крошечным изменением программы DATAGEN.BAS, что генерит из дампа DATA-строки на бейсике. В итоге, после таких манипуляций получаем готовый к вставке в исходник фрагмент. Т.к ассемблерная процедура странслирована на конкретный адрес, то в программу полезно вставить проверку, что нач.адрес инлайн процедуры не сдвинулся, узнавая адрес её размещения функцией ADDR.

Ясно, что такой способ написания вставок на ассемблере с высчитыванием вручную адресов меток для Паскаля МТ+ трудоёмкий и неудобный. Не намного удобнее транслировать ассемблерную вставку обычным ассемблером и получив листинг трансляции смотреть в нём абсолютные адреса и подставлять их числами в паскалевский текст ассемблерной вставки.

Что касается CP/M компиляторов Си. Их имеется с десяток. Из них всего несколько - для КР580. Обычно для Z80 используют HiSoft Си, а для КР580 BDS Си. Для КР580 в начале 80-тых были написаны AZTEC и BDS С. Именно они были заимствованы отечественными "разработчиками" и под другими именами входили в дистрибутив СМ-1800, а позднее использовались и на бытовых ЭВМ.

К сожалению дисководы, а вместе с ними и CP/M стали доступны любителям уже под занавес, в 1993-1995, как раз тогда, когда в страну хлынуло дешёвое западное IBM железо. То что устарело и стало хламом на Западе задешево хлынуло в Россию, отчего 8-ми разрядки сразу же умерли. Этим я хочу сказать, что как Паскаль, так и Си любители программирования просто не успели освоить.

Так что ЯВУ, по-крайней мере, для программирования любительских 8-ми разрядок почти не использовалось. Лишь для ОРИОНА было написано две игры на BDS Си и ещё несколько игр было написана на самодельном Best-Си. А несколько попыток написания на Си системных программ были лишь частично успешными. Лично я написал на Паскале текстов редактор (10 кб), а на Си нортон (30 кб) и видел ещё несколько аналогичных программ других людей. Обычно использовали BDS С или Турбо-Паскаль.

Выяснилось, что при Си и Паскале с ростом сложности программы объём кода нарастает слишком быстро, делая бессмысленной дальнейшую разработку. Например, в 1996 я написал нортон на Си. Сделал лишь часть того, что хотел, а объём кода уже достиг 30 кб. Для нортона это фатально, т.к остаётся совсем мало ОЗУ под буфер открытия окон и, чем меньше дисковый буфер, тем медленнее копирование.

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

На ЯВУ можно писать хорошие системные программы только, если есть развитый ROM-BIOS (или заменяющий его консольный драйвер) и для двухбанковой системы. Тогда в одной банке огромный TPA позволяет уместить большую программу, а в другой банке размещается экран, мощный цветной оконный экранный драйвер и там же буфер сохранения окон. При этом программа на ЯВУ вообще не тратит ресурсы на интерфейс.

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

Только с таким двухбанковым железом и мощным графическим ROM-BIOS можно писать хорошие системные программы на ЯВУ. Тогда на ЯВУ пишется только логика программы и нет траты ресурсов на интерфейс. А когда ничего такого нет и весь оконный интерфейс тоже приходится писать на Си, объём кода быстро раздувается за пределы допустимого, что делает саму программу бессмысленной.

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

PL/M был популярен в 1973-1979, но после появления Паскаля и Си быстро утратил популярность, хотя и в 80-тые немного использовался для программирования однокристальных контроллеров. А вот для текстовой машины, где скорость вывода на экран в 50 раз выше, скоростей ЯВУ может хватить даже для не самых динамичных игр.

Насчёт компиляторов бейсика. В начале 90-тых были доступны только BASCOM и CBASIC. И на оба из них не было документации. По счастью BASCOM был совместим с MBASIC, а он имел полный аналог для MSDOS (для которого документации хватало).

А с CBASIC никто так и не сумел разобраться. Хотя есть информация, что он эффективнее. Сейчас на CBASIC полно документации и при желании можно разобраться. Еще есть Tarbell Basic, его хвалили в журналах, но кажется он хуже. Есть также BASIC-E копилятор. И все они для КР580.

BASCOM позволяет очень быстро разрабатывать и отлаживать программы, хотя скорость откомпилированной программы растёт всего в 2-3 раза, относительно интерпретатора. А относительно компиляторов Паскаля и СИ результирующий код из под бейсика проигрывает в 1.5-2 раза по объёму кода и скорости прогона. Но это компенсируется удобством отладки в режиме интерепретации.

Вот почему мелкие инструментальные програмки удобнее писать на бейсике-компиляторе. И лучше на BASCOM, т.к тогда программа без переделок перетранслируется в Quick Basic или Power Basic на IBM PC. Причём Power Basic есть и в варианте для Windows (а не только для MSDOS).

Можно действовать и наоборот. Написать и отладить программу на IBM PC в Power Basic-е, а затем уже для использования в реале на 8-ми разрядке, странслировать её CP/M-компилятором BASCOM.


Last edited by barsik on 06 Nov 2018 00:05, edited 9 times in total.



29 Apr 2018 13:14
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
barsik wrote:
На КР580 тоже можно написать перемещаемую программу, для чего достаточно иметь внешнюю подпрограмму возвращающую в регистре HL адрес с которого его вызвали...

А мы тут на форуме решили уже давненько эту задачу для более жестких условий.

barsik wrote:
А ведь считается, что Си даёт меньший объём кода, чем Паскаль (но это надо проверить...

Для компиляторов х86 - это действительно так. И меньший и более эффективный код делает Си.
Знал я в былые времена одного человека у себя на работе, который это просто проверил.

_________________
iLavr


29 Apr 2018 14:00
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 584
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Lavr wrote:
barsik wrote:
На КР580 тоже можно написать перемещаемую программу, для чего достаточно иметь внешнюю подпрограмму возвращающую в регистре HL адрес с которого её вызвали...

А мы тут на форуме решили уже давненько эту задачу
.

Нет, вы вовсе не решили задачу как делать перемещаемые программы для КР580,
а вот Е.Седов действительно её решил, причём ещё в начале 90-тых
.

Поясняю его метод. Достаточно в ПЗУ (или в ОЗУ по фиксированному адресу) разместить крошечную подпрограмму в 2 команды, которая возвращает в HL адрес возврата. Узнав таким образом адрес, куда сейчас загружен код программы, вычисляется адрес перехода. Можно использовать самомодифицируемый код, но это сложнее, потому Е.Седов в качестве команд переходов использует условные RET-ы. Два PUSH-POP-а для HL и BC нужны только, если нужно сохранять содержимое регистров. Учтите, что ADD HL,RR портит флаг CY (нужно сохранять AF, если переход по флагу CY).

Такие программы неприятно дизассемблировать, их приходится анализировать вручную считая адреса, что очень утомляет, но зато код работает в любых адресах.

Вот чем по методу Е.Седова заменяется одна команда JP Z,TARGET:

Code:
.
; Перемещаемая команда: JP Z,TARGET
       
        PUSH    HL
        PUSH    BC
        LD      BC,TARGET-M1
        CALL    GETADR
M1:
        ADD     HL,BC
        POP     BC
        PUSH    HL      ; в стеке адрес TARGET
        RET     Z
        POP     HL
        POP     HL
        . . . . .

TARGET: POP     HL
        . . . . .

GETADR: POP     HL      ; п/п-мма по фиксированному адресу
        JP      (HL)


01 May 2018 02:21
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
barsik wrote:
Lavr wrote:
barsik wrote:
На КР580 тоже можно написать перемещаемую программу, для чего достаточно иметь внешнюю подпрограмму возвращающую в регистре HL адрес с которого её вызвали...
А мы тут на форуме решили уже давненько эту задачу.
Нет, вы вовсе не решили задачу как делать перемещаемые программы для КР580,
а вот Е.Седов действительно её решил, причём ещё в начале 90-тых
.

Объясните - иначе голословно. Мы решили эадачу для самого общего случая:
Quote:
...программа загружена, получает управление и при этом
- операционная система неизвестна;
- карта распределения памяти неизвестна;
- наличие стандартных функций или вызовов также неизвестно

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

И если уж обращаться к внешним подпрограммам, то достаточно знать одну внешнюю точку по абсолютному адресу, где есть инструкция RET.
Г-н Е.Седов и до этого не догадался?
А мы специально отказались и от этого упрощения при нахождении абсолютного адреса, куда загружена наша перемещаемая программа.

Так что объясните уж Вашу позицию с фактами.

_________________
iLavr


01 May 2018 09:53
Profile
Doomed
User avatar

Joined: 19 Feb 2017 03:46
Posts: 584
Location: Санкт-Петербург, Россия, третья планета от Солнца, галактика Млечный Путь
Reply with quote
Lavr wrote:
Объясните - иначе голословно
Как же голословно? Позиционно-независимые программы Е.Седова для RK-DOS с успехом используются уже более 25 лет. А всё что вы сделали, это зачем-то за счёт поиска сигнатуры нашли адрес загрузки кода (что вообще делается одной командой). Где же здесь у вас позиционная независимость и чем же это лучше, чем традиционная автонастройка программ на адрес по таблице коррекции?

Согласитесь, что искать сигнатуру (что вообще бессмысленно) и потом корректировать код намного сложнее , вместо того чтобы положить в любом месте ОЗУ (под RAMTOP или на 0) два байта (POP HL : JP (HL)) и сделать туда CALL. К тому же программа по доктрине Седова сразу работает, не нуждаясь ни в каких автонастройках по таблицам. А вашу программу сложнее написать (составляя таблицы коррекции) и громоздко использовать.

Единственный случай, когда поиск кода своей программы был оправдан, это в ОРИОНЕ старт программ для банки 0 из любой банки ОЗУ. Загрузчик Н.Ефимова (1991) служащий для считывания МГ-программ сразу снабжал их универсальным стартером, который позволял запуск кода с адреса 100 в любой банке ОЗУ. Он перекидывал код на рабочие адреса в банку 0, причём не зная изначально в какой банке программа запущена. Т.к для ОРИОНА есть CP/M для банки 0, для банки 1 и для банки 2 (для банки 3 нет). Стартеру требовалось вычислить текущую банку, чтобы программа перегрузки кодов могла работать. Подобную процедуру я встраиваю в программы для DOS банки 0, т.к расширение у всех файлов COM, имена (напр. NC) тоже одинаковые и при запуске не в той банке будет улёт.

Lavr wrote:
- операционная система неизвестна;
- карта распределения памяти неизвестна;
- наличие стандартных функций или вызовов также неизвестно

Реальная программа стартует на реальном компьютере и уж стандартные п/п-ммы ПЗУ и RAMTOP всегда известны. Вы занялись ненужными теоретическими изысканиями.

Lavr wrote:
В нашем случае не нужна никакая внешняя подпрограмма, возвращающая в регистре HL адрес с которого её вызвали.

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

Lavr wrote:
А дальше - стандартная правка абсолютных адресов переходов. Если мы этого не дописали - то это просто других вариантов не имеет.

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

Lavr wrote:
И если уж обращаться к внешним подпрограммам, то достаточно знать одну внешнюю точку по абсолютному адресу, где есть инструкция RET. Г-н Е.Седов и до этого не догадался?

Е.Седов для достижения максимальной эффективности применял много нетривиальных трюков (отчего его программы трудно понимать), но уж явных ошибок никогда не делал. При одной команде RET расход в большее число команд и байтов, и учтите, что этот код повторяется для каждого перехода, т.е вы с'экономили один байт "тут", но потеряли сотни байтов "там".

А факты таковы, что если мне надо написать позиционно-независимую программу, то я напишу макрокоманды для всех нужных мне команд переходов (JP, JP NZ, JP Z, JP NC, JP C) и далее буду писать программу как обычно. Т.о разработка такой программы даже не вызовет ни малейших хлопот с моей стороны. Хлопот даже меньше, чем при Z80, т.к не надо заботиться, чтобы адреса переходов не вышли за диапазон +128 и -127. Макроассемблер всё сделает сам.

А ваш метод так и останется слишком громоздким и бесполезным для практики. Точнее у вас это даже не метод, а просто процедура нахождения сигнатуры. Вместо разработки метода, вы к стандартному методу таблиц придумали вариацию по вычислению адреса загрузки.


Last edited by barsik on 03 May 2018 10:06, edited 1 time in total.



01 May 2018 11:37
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
barsik wrote:
Lavr wrote:
Объясните - иначе голословно
Как же голословно? Позиционно-независимые программы Е.Седова для RK-DOS с успехом используются уже более 25 лет. А всё что вы сделали, это зачем-то за счёт поиска сигнатуры нашли адрес загрузки кода (что вообще делается одной командой). Где же здесь у вас позиционная независимость и чем же это лучше, чем традиционная автонастройка программ на адрес по таблице коррекции?.

Извините, barsik, вы написали много слов, но из того, что мы сделали вы не поняли ровным счетом ничего...

Позиционно-независимые программы Е.Седова используют внешнюю подпрограмму, возвращающую в регистре HL адрес с которого её вызвали.
Он сделал её функцией DOS, как вы пишите, но чего же он тогда просто не добавил в DOS перемещающий загрузчик? :o
Раз уж влезать в системные программы и ПЗУ - всё можно сделать внутри них.

Основной вопрос - откуда узнать абсолютный адрес для этой самой "традиционной автонастройка программ на адрес по таблице коррекции".

Г-н Е.Седов решил пропатчить системное ПЗУ РК-86. Ну хорошо - пусть программа корректно написана
и корректно использует только системные вызовы.
Я запускаю программу, составленную по методу Е.Седова на "Специалисте" - а там в ПЗУ нет
такой подпрограммы, и весь метод Е.Седова - коту под хвост.
Точно так же и на какой-либо случайной "Микроше", "Орионе" и т.д.

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

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

Уникальность того, что мы предложили в том, что "абсолютный адрес для автонастройки программ
на адрес по таблице коррекции" мы находим не зависимо от системного ПЗУ или операционной системы.

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

И наш метод будет работать на всех системах, где есть процесор 8080 или z80.
А метод Е.Седова привязан только к тем системам, где в ПЗУ есть "внешняя подпрограмма, возвращающая в регистре HL адрес с которого её вызвали."
Если её нет - всё! Про метод Е.Седова можете забыть, сколько бы хвалебного многословия вы о нем тут бы не писали.


Ну и раз вы такой умный - покажите, как найти одной командой адрес загрузки кода ?

_________________
iLavr


02 May 2018 08:33
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 27 posts ]  Go to page 1, 2  Next

Who is online

Users browsing this forum: No registered users and 12 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:  
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.