RUЭВМ
Вы хотите отреагировать на этот пост ? Создайте аккаунт всего в несколько кликов или войдите на форум.
Март 2021
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
293031    

Календарь Календарь

Последние темы
» Анонсы монет.
автор Viktor2312 Вчера в 14:30

» Team Red Miner
автор Viktor2312 Вс Фев 28 2021, 17:43

» Пул. beepool
автор Viktor2312 Вс Фев 28 2021, 15:16

» Пул. 2miners
автор Viktor2312 Вс Фев 28 2021, 15:06

» Пул. 0769.it
автор Viktor2312 Вс Фев 28 2021, 14:56

» T-Rex
автор Viktor2312 Вс Фев 28 2021, 14:41

» SRBMiner
автор Viktor2312 Вс Фев 28 2021, 13:53

» PhoenixMiner
автор Viktor2312 Вс Фев 28 2021, 13:26

» NPlusMiner
автор Viktor2312 Вс Фев 28 2021, 13:14

» NiceHash-Miner-Legacy-Fork-Fix
автор Viktor2312 Вс Фев 28 2021, 13:02

» NiceHash-miner
автор Viktor2312 Вс Фев 28 2021, 12:53

» NBMiner
автор Viktor2312 Вс Фев 28 2021, 12:42

» Nanominer
автор Viktor2312 Вс Фев 28 2021, 12:23

» MindMiner
автор Viktor2312 Вс Фев 28 2021, 12:03

» miniZ
автор Viktor2312 Вс Фев 28 2021, 11:32

» lolMiner
автор Viktor2312 Вс Фев 28 2021, 11:01

» KBMiner
автор Viktor2312 Вс Фев 28 2021, 10:58

» Hash Auger
автор Viktor2312 Вс Фев 28 2021, 10:33

» Пул. newpool.pw
автор Atari1974 Вс Фев 28 2021, 10:05

» GMiner
автор Viktor2312 Вс Фев 28 2021, 00:30

» EWBF
автор Viktor2312 Вс Фев 28 2021, 00:28

» Ethminer
автор Viktor2312 Сб Фев 27 2021, 19:57

» Ищу файл печатной платы
автор freddy Пт Фев 26 2021, 14:01

» Языки программирования. Статьи, заметки, очерки, разное...
автор Viktor2312 Чт Фев 25 2021, 19:52

» CryptoDredge
автор Viktor2312 Чт Фев 25 2021, 18:30

Самые активные пользователи за месяц
Viktor2312
Компиляторы Си для программирования РК86 Vote_l10Компиляторы Си для программирования РК86 Voting10Компиляторы Си для программирования РК86 Vote_r10 

Поиск
 
 

Результаты :
 


Rechercher Расширенный поиск


Компиляторы Си для программирования РК86

Перейти вниз

Компиляторы Си для программирования РК86 Empty Компиляторы Си для программирования РК86

Сообщение  barsik Вт Дек 08 2020, 08:42

1
В среде CP/M копиляторов Си в коды КР580 больше, но наибольшее применение нашли два таких компилятора BDS C и AZTEC C (остальные компиляторы большей частью некоммерческие, а любительские и производят впечатление игрушек). Я не знаю какой из них лучший, потому что знал хороших программистов, что выбрали BDS C и достигли успехов, но также знал и тех программистов, кто использовал AZTEC и тоже достигли больших успехов. Например, известные программисты любители из г.Тула, подписывающиеся Adelaide corp, используя AZTEC в версии для Z80 написали графическую Windows-библиотеку, что позволяла не напрягаясь писать для ОРИОНА программы с окнами и меню. Для AZTEC-а была написана граф.библиотека Тужилина, а для BDS - граф.библиотека В.Широкого.  В то же время и на BDS были написаны, как минимум, два нортона, два текстовых редактора, редактор секторов дискеты и другие программы. Сам я в середине 90-тых годов также использовал BDS, а AZTEC не использовал.

Похоже, что BDS C это хороший компилятор ЯВУ вполне подходящий для штамповки игр для РК86. Скажем спасибо за это Леору Золману (фото). Точнее я смогу сказать о качествах этого компилятора, когда напишу на нём несколько динамичных игр. Отзывы противоречивы (вроде бы BDS-C был очень популярным в начале 70-тых, но после скис и встречал упоминания, что в нём есть глюки). Хотя, если бы компилятор был плох, то автор не смог бы торговать им за $100, как минимум с 1979 по 1989 год, (т.е пока на Западе в пользовании у населения были 8-ми разрядки).

Лет 25 назад я уже немного программировал на Си для 8-ми разрядки (и как раз на BDS-C). Си - прост, чего нельзя сказать про C++. По счастью компилятора C++ для 8-ми разрядок нет (компилятор C++ невозможен на 8-ми разрядке ввиду малости адресного пространства, а выходной код из под C++ не уместился бы в ОЗУ).

Для хорошего инструмента плотность выходного кода конечно важна, но важнее скорость разработки (небольшую нехватку скорости можно выровнять заплатками на ассемблере). Во многом лучший инструмент для изготовления игр 8-ми разрядок это Турбо-Паскаль. Т.к это именно Паскаль, а не Си, то новичками он осваивается быстрее и даёт код ничуть не хуже, чем Си для Z80.

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

Но увы, применительно к РК86 Турбо-Паскаль отпадает, т.к требует Z80, а в РК86, как назло, применён какой-то левый процессор. Это обстоятельство исключает TP из претендентов на инструмент для базового РК. Настоящие любители ретро программирования меняют неудачный процессор в РК86 на Z80 и даже не столько из-за его системы команд, сколько из-за большего быстродействия. Это бы не пришлось делать, если бы 4.0 или хотя-бы 3.1 мегагерцовые CPU 8080 были доступны.

Для КР580 есть ещё Aztec C, который тоже вполне профессиональный инструмент. Но всем кто сравнивал, Aztec нравится меньше, почему-то он менее популярен. Т.о BDC-C это и есть лучший инструмент программиста для РК86. Конечно для КР580 есть ещё и Паскаль МТ+, но он по моим данным даёт код чуть менее плотный, чем BDS-С, и сделан в стандартах 70-х годов, так что для наших целей он хуже. Впрочем, каким инструментом из подходящих для решения конкретной задачи лучше умеешь пользоваться, - тот для тебя и лучший.

А вот компиляторов Си для Z80 довольно много (можно посмотреть здесь). Есть и кросс компиляторы для PC, есть и для CP/M. Увы, для РК годятся только самые древние компиляторы предназначенные ещё для процессора 8080 (например).

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

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

К сожалению, мои исходники программ написанных на BDS-C в 90-тые (а их было и немного - текстов редактор и нортон) сейчас недоступны (т.к после множества крахов винтов они остались лишь на резервных дискетах в формате ОРИОНА, формат полу-HD нестандартный, читалками дискет не считать, теледиск тоже такой формат не берёт).

И всё же один пример программы на BDS-C на винте сохранился. Это исходник нортона для ОРИОНА. К сожалению, это лишь стартовая версия совсем без вкраплений ассемблера и вероятно далеко не полный функционал (размер кода всего 17 кб, а окончательная версия превысила 32 кб, причём из которых ~треть С-кода была переписана на ассемблере). В этой версии ещё и окон нет, ввод пользователя делается не в окне, а в нижней служебной строке (называемой bottom), кстати, в нижней строке даже нет подсказок и Бог знает чего ещё. Если найду энтузиазм починить ОРИОН и главное, КНГМД на ВГ93 (который сдох более 15 лет назад), то скачаю с дискет архивы своих программ для ОРИОНА на Турбо-Паскале и Си (но этим займусь вряд-ли вскоре, т.к это электротрах на много дней).

Это двухпанельный нортон, похоже он делался CP/M-корректным (т.е в железо машины не лезет). Еще не особо вникал, - он состоит всего из нескольких модулей. Основной модуль и набор ASM-подпрограмм (там интерфейс с ROM-BIOS и ассемблерная процедура рисования двух панелей, т.к их рисование кодом на Си в несколько раз тормознее). А при отладке при необходимости подключается модуль OTLAD.C, что позволяет просмотреть дамп.

Наличие исходника реальной программы очень кстати. Наличие всего единственного примера мне кардинально упрощает изучение BDS-C, это с'экономит десятки часов чтения документации. Лишь чуть пролистав исходник, я понял как в программы на BDS-C включаются фрагменты на ассемблере, а из SUB-файлов узнал команды трансляции. К сожалению, обнаружил, что BDS-C не только генерит код для КР580, но и исходники на ассемблере для него надо писать в мало кому понятных мнемониках ассемблера КР580, ведь большинству людей привычна более удобная мнемоника Z80.

Пока достал своего пожелтевшего "Кернигана и Ритчи" первое издание (1984), бумага некачественная, он пожелтел уже в начале 90-тых. Есть и второе издание (1992) (оно уже на хорошей глянцевой бумаге), но второе издание не для ретро CP/M-версий компилятора, а для компиляторов на IBM PC, т.к там описывается уже новый стандарт. А также достал распечатку документации к BDS-C. Но теперь, благодаря наличию примера программы на BDS-C, особо штудировать доки вряд-ли понадобится. Чтобы вспомнить что-то попробую разобраться в своей же программе 25-ти летней давности. Если кому интересно, вот основной модуль этого нортона.

основная программа:

#include <BDSCIO.H>

#define NOASM 0
#define OTLAD 0
#define F81B_DIRECT 1

#include <STCONST.H>

char posstr[5];
char fcb1[36], joker[16];
char *pstr, *fptr;
char last;                      /* для type */
int cpan;                       /* 0/1 текущая панель */
int pdone1, pdone2;             /* флаг что панель выведена */
int getfl1, getfl2;             /* флаг что кат. считан */
int fflag1, fflag2, fflag;      /* фл.первого обращения к панели */
int disk1, disk2, disk;
int user1, user2, user, in_user;
int files1, files2, files;
int ofs1, ofs2, offs;
int pos1, pos2, pos;
int pofs;
unsigned mszlow,mszhig;

char txt[48];
char fname[30];

int fd1,fd2;
int bufsects;                   /* сколько секторов в буфере */
unsigned bufsize;               /* размер буфера */
unsigned corebuf;               /* адрес начала буфера */
char sourname[30];
char destname[30];
char verify, *vbuf;

struct NREC {
 char NAME[8];
 char TYP[3];
 char MARK[1];
 char noneed[2];
 char RECS[1];
};

struct NREC kat1[MAXFILES];
struct NREC kat2[MAXFILES];

main()
{

char ch, *p;
int i, tmpcpan;
char ptitr[12];

fflag1=fflag2=0; disk1=1; disk2=2;
user1=in_user=bdos(fuser,0xFF); user2=0;
bios(4,27); bios(4,0x30); bios(4,27); bios(4,0x3a);

verify=1;

if (verify) vbuf = sbrk(SECSIZ);
    corebuf = sbrk(SECSIZ); /* адр.начала буфера */

/* получить буфер максимального размера */
for (bufsize = SECSIZ; sbrk(SECSIZ) != ERROR; bufsize += SECSIZ)
  ;
  bufsects = bufsize / SECSIZ;

clrscr();

pstr= &posstr;
*pstr =0x1B; *(pstr+1)=0x59; *(pstr+4)=0;

RESTAR:

pdone1=pdone2=0;
getfl1=getfl2=0;
dskreset();

RENEW:

fptr=fcb1;
if (!getfl1) if (!getkat(0,disk1)) error("Bad 1 FCB init");
if (!getfl2) if (!getkat(1,disk2)) error("Bad 2 FCB init");

LOO1:
#if    NOASM
rames();
#else
panel(0);
gotoxy(0,lp_ofs+7); printf(" %c:\\User %d ",'@'+disk1,user1);
panel(1);
gotoxy(0,rp_ofs+7); printf(" %c:\\User %d ",'@'+disk2,user2);
#endif

PLOOP:
setprm(cpan); tmpcpan=cpan;
if (pdone1==0)  { setprm(0); catout(); }
if (pdone2==0)  { setprm(1); catout(); }

setprm(tmpcpan); restbtm();
ch=select();
getprm();
switch(ch)
{
case 1: if (cpan) getfl2=pdone2=0; else getfl1=pdone1=0;
        goto RENEW;
case 2: if (cpan) getfl1=pdone1=0; else getfl2=pdone2=0;
        goto RENEW;
case 3: if (cpan==0) cpan=!cpan; else cpan=0; goto PLOOP;
case 4: if (cpan) getfl1=pdone1=pdone2=0;
           else getfl2=pdone2=pdone1=0;
        goto RENEW;
case 5: goto RESTAR;
case 6: pdone1=pdone2=0; goto LOO1;
case 27: goto M1;
}

M1:
gocpm();

}

/* -------------------------- */

setprm(panel)
int panel;
{ cpan=panel;
if (cpan==0)
  {
   disk=disk1; user=user1; pos=pos1; pofs=lp_ofs; fflag=fflag1;
   files=files1;
   if (!fflag) { offs=0; fflag1=-1; } else offs=ofs1;
  }
else
  {
   disk=disk2; user=user2; pos=pos2; pofs=rp_ofs; fflag=fflag2;
   files=files2;
   if (!fflag) { offs=0; fflag=-1; } else offs=ofs2;
  }
}

getprm()
{
if (cpan==0)
  {
   disk1=disk; user1=user; pos1=pos; fflag1=fflag; ofs1=offs;
  }
else
  {
   disk2=disk; user2=user; pos2=pos; fflag2=fflag; ofs2=offs;
  }
}

int getkat(np,nd) /* np=0/1   nd=1/2/3 */
int np,nd;
{ int usr,i;
char *p;

if (np==0) { usr=user1; files1=0; getfl1=pdone1=0; }
  else { usr=user2; files2=0; getfl2=pdone2=0; }
clrcat(np);

p=podgot(nd,usr);
/* p=&fcb1; */
if (setfcb(p,joker)!=0) return(0);

i=bdos(ffirst,fcb1);
if (i==0xFF) return(1);  else putname(i,np);
while ( (i=bdos(fnext,fcb1)) != 0xFF ) putname(i,np);
if (np==0) getfl1=1; else getfl2=1;
return(1);
}

char *podgot(disk,usr)   /* подготовить FCB для first/next */
int disk,usr;
{
switch(disk)
 {
 case 1: strcpy(&joker,"A"); break;
 case 2: strcpy(&joker,"B"); break;
 default: error("Wrong drive N");
 }
strcpy(&joker[1],":????????.???");

dma80(); bdos(fuser,usr);
fptr = &fcb1; fillmem(fptr,36,0);
return(&fcb1);
}

gotoxy(x, y)
char x,y;
{ char *pstr;
pstr = posstr;
*(pstr+2) = 32 + x;
*(pstr+3) = 32 + y;

#if     NOASM
  while (x = *pstr++) bios(4,x);
#else
  mssg(pstr);
#endif

}

inverse()
{ bios(4,0x1B); bios(4,0x36); }

normal()
{ bios(4,0x1B); bios(4,0x37); }

copymem(d,s,n)
char *d, *s;
int n;
{
 while (n!=0)
   { (*d++) = (*s++); n--; }
}

clrcat(n)
{
int i;
char *p;
if (n==0)
  { files1=0; for (i=0; i < MAXFILES; i++) { p=&kat1[i]; *p=0; }
  }
else
  { files2=0; for (i=0; i < MAXFILES; i++) { p=&kat2[i]; *p=0; }
  }
}

int putname(of,np)
int of,np;
{ char *d, *s;
if ((np==0) && (files1>=MAXFILES-1)) return(0);
if ((np) && (files2>=MAXFILES-1)) return(0);
s=0x80+(32*of)+1;
if (np==0) d=&kat1[files1]; else d=&kat2[files2];
copymem(d,s,15);
if (np==0) d=&kat1[files1++].MARK; else d=&kat2[files2++].MARK;
*d=0; return(1);
}

catout()
{
int n;
n=0;

if (cpan==0) pdone1=1; else pdone2=1;
if (files==0) { for (n=0; n<VSIZE-3; n++) blankout(n); return; }
for (n=0; n<VSIZE-3; n++)
  if (n+offs < files) filout(n,n+offs); else blankout(n);
}

filout(lin,n)
int n,lin;
{
int i;
unsigned len;
char *p;

gotoxy(1+lin,1+pofs);
if (cpan) p=&kat2[n].MARK; else p=&kat1[n].MARK;
if (*p) bios(4,'*'); else spc();
if (cpan) p=&kat2[n]; else p=&kat1[n];
if (*p==0) { blankout(lin); return; }
prfname(p,0);
if (cpan) p=&kat2[n].RECS; else p=&kat1[n].RECS;
len=(*p)*128; printf(" |%6d ",len);
}

balkout(lin,n)
int n,lin;
{
int i;
unsigned len;
char *p;

gotoxy(1+lin,1+pofs); inverse();
if (cpan) p=&kat2[n].MARK; else p=&kat1[n].MARK;
if (*p) bios(4,'*'); else spc();
if (cpan) p=&kat2[n]; else p=&kat1[n];
if (*p==0) { normal(); blankout(lin); return; }
prfname(p,1);
if (cpan) p=&kat2[n].RECS; else p=&kat1[n].RECS;
len=(*p)*128; printf(" |%6d ",len);
normal();
}

prfname(uk,balkflg)
char *uk;
int balkflg;
{ int i;
for (i=0; i<8; i++) prnmal(*uk++,balkflg);
bios(4,'.');
for (i=0; i<3; i++) prnmal(*uk++,balkflg);
}

prnmal(ch,prm)
char ch,prm;
{
 if ((prm) || (ch>0x80)) inverse(); else normal();
 ch &= 0x7F;
 if ((ch<='Z') && (ch>'@')) ch |= 0x20;
 if (ch<32) bios(4,'?'); else bios(4,ch);
 if (prm==0) normal();
}

blankout(n)
int n;
{
gotoxy(1+n,pofs+1);
nspc(14); bios(4,'|'); nspc(6);
}

cr()
{ bios(4,13); bios(4,10); }

spc()
{ bios(4,' '); }

nspc(n)
{
 while (n>0) { bios(4,' '); n--; }
}

select()
{
char *p, *d, ch;
int i;

if (fflag==0) { pos=0; fflag=!0; }
view();

while (1)
{
  if (kbhit()) getchar();
if (files==0) { pos=0; offs=0; inverse(); gotoxy(1,pofs+1);
printf(" No files     |       "); normal(); goto MMM; }
if (pos>VSIZE-4) pos=VSIZE-4;
if (pos+offs >= files)
   if (files>VSIZE-3) offs=files-(VSIZE-3);
    else { offs=0; pos=files-1; }
balkout(pos,pos+offs);
MMM:
ch=getkey(); if (ch!=32) ch &= 0x5F;
bios(4,7);
filout(pos,pos+offs);
switch(ch)
 {
 case K_UP: if (pos>0) pos--;
    else if (offs>0) { offs--; catout(); } else beep(); break;
 case 32:
      if (cpan) p=&kat2[pos+offs].MARK; else p=&kat1[pos+offs].MARK;
      if (*p==0) *p=0xff; else *p=0; filout(pos,pos+offs);
      view(); ch=K_DN;
 case K_DN: if ((pos<VSIZE-4) && (pos+offs < files-1)) pos++;
              else if (offs+VSIZE-3 < files) { offs++; catout(); }
                      else beep();
          break;
 case K_LF: if (pos==0) { offs=0; pos=0; catout(); }
               else pos=0; break;
 case K_RT: if (files >VSIZE-3)
              {
               if (VSIZE-3+offs < files)
                 if (pos!=VSIZE-4) pos=VSIZE-4;
                   else if (offs+VSIZE-3+VSIZE-3 < files)
                           { offs += VSIZE-3; catout(); }
                        else { offs=files-(VSIZE-3);
                               pos=VSIZE-4; catout(); }
               }
               else pos=files-1; break;
 case K_WK: return(0);
 case 10: return(0);
 case K_TB: return(3);
 case 'E':  i=getmarked(0);
         if (i) if (grera()==27) break; else return(1);

         bottom();
         if (getname(pos+offs))    /* если ф.R/O */
          { printf("File %s is R/O. Erase (Y/N) ? ",sourname+4);
            ch=getyes(); restbtm(); if (ch!='Y') break;

            strcpy(&joker,&sourname[2]);
            bdos(fuser,user);
            p = &fcb1; fillmem(p,36,0);
            if (setfcb(p,joker)!=0)
            error("setfcb");

            fcb1[9] &= 0x7F;
            bdos(fsetatr,fcb1);
            goto MTK_E1;
          }
         printf("Erase %s (Y/N) ? ",sourname+4);
         ch=getyes(); restbtm();
         if (ch!='Y') break;
MTK_E1:   erase(sourname); return(1);

 case 'D': case 'd': ch=askdisk(); if (ch==27) break;
           disk=ch; dskreset(); return(1);
 case 'U': case 'u': if ((i=askuser())==27) break;
           user=i; return(1);
 case 1: return(5);
 case 27: return(27);
 case 'C':

/* Проверка что не копирование "в себя" */

 if (cpan==0) { if ((disk==disk2) && (user=user2)) break; }
   else if ((disk==disk1) && (user==user1)) break;

   i=getmarked(0);
 if (i != 0) { grcopy(); restbtm(); return(4); }

 getname(offs+pos); formtarget(); copying(); restbtm();
 return(2);

case 'T': getname(offs+pos); typing(); clrscr(); return(6);
default: beep(); beep(); /* if (pos<VSIZE-4) pos++; */
 }
}
}

view()
{ int i;
 gotoxy(2,50); printf("Files:     ");
 gotoxy(2,50+8); printf("%d",files);
 i=cntmrk();
 gotoxy(3,50); printf("Marked:   ");
 gotoxy(3,50+8); printf("%d",i);
 gotoxy(4,50); printf("Size:         ");
 gotoxy(4,50+8);
 if (mszhig) printf("%d",mszhig);
 if (mszlow) printf("%03d",mszlow); else bios(4,'0');
}

int getname(n)  /* возвр. 0 если файл R/W */
int n;
{ char *p, *d, ch;

 if (cpan==0) p=&kat1[n]; else p=&kat2[n];
   d=&sourname[0];
   strcpy(d,"0/A:\0x00"); *d=user+'0'; *(d+2)='@'+disk;
   d=&sourname[4];
   d=copsym(d,p,8); *(d++) = '.'; copsym(d,p+8,3);
/*    if (sourname[13] & 0x80) return(1); else return(0);
       0/A:12345678.1
       0123456789012345 */

 if (cpan==0) p=&kat1[n]; else p=&kat2[n];
 ch = (*(p+8)) & 0x80; if (ch==0) return(0); else return(1);
}

formtarget()
{
 strcpy(destname,sourname);
 if (cpan==0) { destname[0]='0'+user2; destname[2]='@'+disk2; }
    else { destname[0]='0'+user1; destname[2]='@'+disk1; }
}

int grcopy()
{ int i;
 char ch;
 if (getmarked(0)==0) return(0);
 bottom();
 if (cpan) ch='@'+disk1; else ch='@'+disk2;
 printf("Copy %d files to %c: (Y/N) ? ",cntmrk(),ch);
 if (getyes() != 'Y') return(0);
 while (1)
  { if (getmarked(1)==0) break;
    formtarget(); i=copying(); restbtm();
    if (i==27) return(27);
/*     unmark(); */
  }
  return(0);
}

int grera()
{ int i;
 char ch;
 if ((i=cntmrk())==0) return(0);
 bottom(); printf("Erase %d files (Y/N) ? ",i);
 ch=getyes(); restbtm();
 if (ch != 'Y') return(27);
 while (1)
    { if (getmarked(1)==0) break; erase(sourname); }
 return(0);
}

unsigned getmarked(sbr_flg)
int sbr_flg;
{
 char *p1, *p2;
 unsigned i;
 for (i=0; i<files; i++)
   {
     if (cpan) { p1=&kat2[i]; p2=&kat2[i].MARK; }
          else { p1=&kat1[i]; p2=&kat1[i].MARK; }

     if (*p1==0) return(0);
     getname(i);
     if (*p2) { if (sbr_flg) *p2=0; return(1); }
   }
 return(0);
}

int cntmrk()
{
 char *p1, *p2, *p3;
 int i,n;
 unsigned sss;
 mszlow=0; mszhig=0;
 for (n=0,i=0; i<files; i++)
   {
     if (cpan) { p1=&kat2[i]; p2=&kat2[i].MARK; p3=&kat2[i].RECS; }
          else { p1=&kat1[i]; p2=&kat1[i].MARK; p3=&kat1[i].RECS; }

     if (*p1==0) break;
     if (*p2)
       {
         n++;
         sss=(*p3)*128; mszhig += sss/1000; mszlow += sss % 1000;
         if (mszlow >= 1000) { mszlow -= 1000; mszhig++; }
       }
   }
 return(n);
}

erase(name)
char *name;
{ if (unlink(name)) error("Bad erase");
   if ((disk1==disk2) && (user1==user2))
          getfl1=getfl2=pdone1=pdone2=0;
  return(0);
}

int copying()
{
int i,j,k;
char ch;
char *dest, *sour;

 dest=&destname; sour=&sourname;
 bottom(); printf("Copying file %s",sour+4);
AAA:
if ( (fd2=open(dest,0) ) != ERROR)
   {
     fabort(fd2);
     bottom(); printf("File %s exists ! Overwrite (Y/N) ? ",dest+4);
     ch = getyes(); restbtm();
     if ((ch==27) || (ch==3)) return(27);
     if (ch=='Y')
       {
         if (unlink(dest)== ERROR)
           { sprintf(txt,"Can't erase %s",dest);
             error(txt); }
         goto AAA;
       }
     return(0);
   }

 if ((fd1=open(sour,0)) == ERROR)
         { sprintf(txt,"Can't open %s",sour);
         error(txt); }
 if ((fd2=creat(dest)) == ERROR)
         { sprintf(txt,"Can't create %s",dest);
           error(txt); }

     /* May be out of directory space */

while (1)
 {
     if (kbhit()) getchar();
        if (!(i = read(fd1,corebuf,bufsects))) break;
        if (i == ERROR)
         {
          sprintf(txt,"Read error: tell(fd1) = %d, \"%s\"\n",
                                    tell(fd1), errmsg(errno()));
          error(txt);
         }

     if (kbhit()) getchar();
     if (write(fd2,corebuf,i) != i)
       {  sprintf(txt,"\nWrite error: %s\n",errmsg(errno()));
          error(txt);
       }

  if (verify)
    {
     seek(fd2, -i, 1);    /* отпозиционировать назад */
     for (j=0, k=corebuf; j<i; j++, k += SECSIZ)
       {
        if (read(fd2, vbuf, 1) != 1)
         { sprintf(txt,"\nVerify error on %s\n", destname);
           error(txt); }
        if (memcmp(vbuf, k, SECSIZ)) continue;
           else { sprintf(txt,"\nVerify error on %s\n", destname);
                  error(txt); }
       }
    }

 }  /* конец блока while(1) */

 if (close(fd2) == ERROR)
         { sprintf(txt,"Can't close file %s",dest);
           error(txt); }

 fabort(fd1); return(0);
}

typing()
{
int i,j,k,first;
char ch, *p;

TOTOP:
first=TRUE;
 p=&sourname;
 if ( (fd1=open(p,0) ) == ERROR)
       { bottom(); printf("Can't open %s",p);
         getkey(); restbtm(); return;
       }

fillmem(corebuf,bufsize,0x1A);
while (1)
 {
        if (!(i = read(fd1,corebuf,bufsects))) break;
        if (i == ERROR)
         {
           sprintf(txt,"Read error: tell(fd1) = %d, %s\n",
                                    tell(fd1), errmsg(errno()) );
           error(txt);
         }

     if (first) clrscr(); first=FALSE;
     if (isnotxt()) j=dumpbuf(corebuf,i); else j=print(corebuf,i);
     if (j==1) break;
     if (j==2) { fabort(fd1); goto TOTOP; }
 }
 fabort(fd1);
}


print(txbuf,sec)
char *txbuf;
int sec;
{ int i,j,py;
 char ch, ch2;
 unsigned u;

 py=0; u = (128 * sec);

 while (1)
 {
   if (*txbuf == 0x1A) { getchar(); return(1); }
   if (u==1) return(0);
/*    if (last!=0) { ch=last; u++; txbuf--; } else */
     ch = *txbuf & 0x7F;

/*
   if (ch==13)
    { cr(); py=0;
PR1:   txbuf++; u--; ch=last=*txbuf;
      if (u==1) return(0);
      if (ch==10) goto PR1;
      if (ch==0x1A) getchar(); return(1);
    }
   if (ch==10) goto PR2;
*/
   if (ch==13) if (kbhit())
                 { if ((ch2=getchar())==27) return(1);
                   free(); ch2=getkey();
                   if (ch2==27) return(1);
                   if ((ch2==K_UP) || (ch2 & 0x5F == 'B')) return(2);
                 }
   if (ch==13)
      { if (py<63) for (i=py; i<63; i++) bios(4,32);
      }
   if ((ch==10) || (ch==13)) py=0;
   if (ch==9)
     {
       i=py % 8;  if (i==0) i=8; else i= 8-i;

       for (j=0; j<i; j++)
          { py++; spc(); if (py>=63) break; }
       goto PR2;
     }
   if (py>=63) goto PR2;
  if ((ch!=13) && (ch!=10)) if (ch<32) ch=32;
   bios(4,ch); py++;
PR2: txbuf++; last = *txbuf;
 }
}

dumpbuf(dbuf,sec)
char *dbuf;
int sec;
{ int i,u,endflg;
 char ch2;

 if (kbhit()) { ch2=getchar(); if (ch2>=32) bios(4,8); }
 if (sec<bufsects) endflg=TRUE; else endflg=FALSE;
 u=0;
 while (sec)
  {
/*
   if (isfree()==FALSE)
                 { if ((ch2=getkey())==27) return(1);
                   free(); ch2=getkey();
                   if (ch2==27) return(1);
                   if ((ch2==K_UP) || (ch2 & 0x5F == 'B')) return(2);
                 }
*/
    if ( i=dump80(dbuf,u) ) return(i);
    dbuf += 128; u += 128; sec--;
  }
 if (endflg)
     { ch2=getyes(); if ((ch2==K_UP) || (ch2 =='B')) return(2); }
 return(0);
}

int isnotxt()
{ char *p;
 if (cpan) p=&kat2[pos+offs].TYP; else p=&kat1[pos+offs].TYP;
 if ( (memcmp(p,"COM",3)) || (memcmp(p,"DAT",3)) ||
      (memcmp(p,"SYS",3)) || (memcmp(p,"OVR",3)) ||
      (memcmp(p,"CRL",3)) || (memcmp(p,"REL",3)) ||
      (memcmp(p,"HEX",3)) || (memcmp(p,"O  ",3)) ||
      (memcmp(p,"OVL",3)) || (memcmp(p,"ORD",3)) ) return(TRUE);
return(FALSE);
}

error(txt)
char *txt;
{ gotoxy(VSIZE-1,0); printf("\nError '%s'",txt); gocpm(); }

char getkey()
{
#if     F81B_DIRECT

char ch;  while ((ch=f81b())==0xFF) ;
while (f81b() != 0xff) ;
return(ch);
#else
 while (bios(2)==0) ;
 return(bios(3));
#endif
}

int isfree()
{
#if     F81B_DIRECT
if (f81b() == 0xff) return(TRUE); else return(FALSE);
#else
 if (bios(2)==0) return(TRUE); else
   { getchar(); return(FALSE); }
#endif
}


free()
{
#if     F81B_DIRECT
while (f81b() != 0xff) ;
#else
 if (bios(2)==0) return();
 while (bios(2)!=0) ;
 getchar();
#endif
}

char getyes()
{ char ch;
 ch=getkey(); if (ch==13) ch='Y';
 return(0x5F & ch);
}

char* copsym(dest,sour,n)
char *dest, *sour;
char n;
{ char ch;
 while (n) if ((ch=*sour++) <= 32) break;
      else { *dest++=ch; n--; }
 *dest=0;
 return(dest);
}

fillmem(adr,siz,byt)
char *adr, byt;
unsigned siz;
{
 while (siz--) { *adr++ = byt; }
}

beep()
{ int i; for (i=0; i<5; i++) bios(4,7);
}

bottom()
{ int i; gotoxy(VSIZE-1,0); for (i=0; i<63; i++) bios(4,' ');
 gotoxy(VSIZE-1,0);
}

char askdisk()
{ char ch;
 while (1)
 {
  bottom(); printf("Select Disk (A..D) ? ");
  if ((ch=(getkey() & 0x5F))==27) break;
  ch=ch-'@'; if ((ch>0) && (ch<4)) break;
 }
  restbtm();   return(ch);
}

int askuser()
{ char ch;
 int i;
 while (1)
 {
  bottom(); printf("Select User (0..9,A..F) ? ");
  if ((ch=(getkey() & 0x5F))==27) break;
  if ((ch>='F') && (ch<'0')) continue;
  if (ch>='A') i=10+(ch-'A'); else
    i = ch-'0'; break;
 }
  restbtm();   return(i & 0xf);
}

restbtm()
{ int i;
 gotoxy(VSIZE-1,0);
 for (i=0; i<63; i++) bios(4,' ');
 gotoxy(VSIZE-1,0);
 bios(4,'@'+disk); bios(4,'>');
}

gocpm()
{
gotoxy(VSIZE-2,0);
bios(4,27); bios(4,0x31); bios(4,27); bios(4,0x3b);
bdos(fuser,in_user); exit();
}

int dump80(adr,padr)
unsigned padr;
char *adr;
{
 int i,j;
 char *tadr, ch2;

 if (kbhit()) getchar();

 for (j=0; j<8; j++)
  {
   printf("\n%04x ",padr);
   tadr=adr;
   for (i=0; i<16; i++) { hex(*adr++); spc(); }
   for (i=0; i<16; i++) prsym(*tadr++);
   padr += 16;

   if (isfree()==FALSE)
                 { if ((ch2=getkey())==27) return(1);
                   free(); ch2=getkey(); bios(4,8);
                   if (ch2==27) return(1);
                   if ((ch2==K_UP) || (ch2 & 0x5F == 'B')) return(2);
                 }
  }
 return(0);
}

#if !OTLAD

hex(ch)
char ch;
{
nibl((0xF0 & ch) >> 4);
nibl(0x0F & ch);
}

nibl(ch)
char ch;
{
if (ch<10) bios(4,'0'+ch);
  else bios(4,ch-10+'A');
}

prsym(k)
char k;
{ if (k<32) bios(4,'.'); else bios(4,k); }

#endif


#if     NOASM
#include "rama.c"
#endif

#if     OTLAD
#include "otlad.c"
#endif

Легко видеть, что реализованы лишь основные команды: D- выбор диска, U- выбор юзера, <ВК>- запуск COM-файла, <пробел>- отметка файлов (для копирования или удал.), C- копирование файлов, T- просмотр файлов (текстовых текстом, кодовых дампом), E- удаление файлов, F2- перечитать диск, <ТАБ>- скачки между панелями, <ESC>- выход в DOS. В ходе отладки программы в реале (без использования отладчика), нужна процедура вывода дампа (которая подключается к программе, если ключ OTLAD=1). Отладчиком я практически не пользовался, тем более для ЯВУ, где стандартный отладчик вообще бесполезен. Если часто перетранслировать в ходе разработки, то и без отладчика ясно в каком месте ошибка, и чтобы понять что не так достаточно просмотреть переменные, для чего и нужен дамп.

просмотр дампа в ходе отладки:

dump(adr,siz)
char *adr;
int siz;
{
int ad,i,j,lns,off,of2;
char *tadr, *adr2;

lns = siz / 16;
if ((siz % 16) != 0) lns++;

off=of2=0;
if (adr > 0x8000) goto M3;
ad = adr;
off = ad % 16; of2=off;
if (off != 0)
   for (i=off; i=0; i--) adr--;
M3:
for (j=0; j<lns; j++)
 {
   ad=adr; printf("\n%x ",adr);
   tadr=adr;
   for (i=0; i<16; i++)
   { if (off==0) hex(*adr++); else { spc(); spc(); off--; } spc(); }
   spc();
   for (i=0; i<16; i++)
      { if (of2==0) prsym(*tadr++); else { spc(); of2--; } }
 }
}

hex(ch)
char ch;
{
nibl((0xF0 & ch) >> 4);
nibl(0x0F & ch);
}

nibl(ch)
char ch;
{
if (ch<10) bios(4,'0'+ch);
  else bios(4,ch-10+'A');
}

prsym(k)
char k;
{ if (k<32) bios(4,'.'); else bios(4,k); }


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

фнкции написанные на ассемблере:

       INCLUDE   <BDS.LIB>

LP$OFS: EQU     0
RP$OFS: EQU     24
V$SIZE: EQU     25

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

        function MSSG

        CALL    MA1TOH
        PUSH    B
        MOV     C,L
        MOV     B,H
        LHLD    1
        LXI     D,9
        DAD     D
        SHLD    EXECAL +1
        MOV     H,B
        MOV     L,C
MSGLOO: MOV     A,M
        ORA     A
        JZ      MSGEXI
        MOV     C,A
EXECAL: CALL    0
        INX     H
        JMP     MSGLOO
MSGEXI:
        POP     B
        RET

        endfunc

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

        function F81B

        CALL    0F81BH
        MVI     H,0
        MOV     L,A
        RET

        endfunc

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

        function PANEL
        external BOTTOM

        CALL    MA1TOH
        MOV     A,L

        PUSH    B
        LHLD    1
        LXI     D,9
        DAD     D
        SHLD    BIO4C

        ORA     A
        JNZ     RPANEL

LPANEL:
        MVI     H,LP$OFS+1
        MVI     L,0
        CALL    HORIZ

        MVI     H,LP$OFS
        MVI     L,0
        CALL    VERT

        MVI     H,LP$OFS+23
        MVI     L,0
        CALL    VERT

        MVI     H,LP$OFS+1
        MVI     L,V$SIZE-2
        CALL    HORIZ

        MVI     H,LP$OFS+15
        MVI     L,0
        CALL    VERT

        MVI     H,LP$OFS+7
        MVI     L,0

        POP     B
        RET

RPANEL:
        MVI     H,RP$OFS+1
        MVI     L,0
        CALL    HORIZ

        MVI     H,RP$OFS
        MVI     L,0
        CALL    VERT

        MVI     H,RP$OFS+23
        MVI     L,0
        CALL    VERT

        MVI     H,RP$OFS+1
        MVI     L,V$SIZE-2
        CALL    HORIZ

        MVI     H,RP$OFS+15
        MVI     L,0
        CALL    VERT

        MVI     H,RP$OFS+7
        MVI     L,0

        POP     B
        RET

VERT:   SHLD    KOO
        CALL    POSIT
        CALL    PLUS
        LDA     KOO
        INR     A
        STA     KOO
VLOOP:  LHLD    KOO
        CALL    POSIT
        MVI     C,'|'
        CALL    CNOUT
        LDA     KOO
        INR     A
        STA     KOO
        CPI     V$SIZE-2
        JNZ     VLOOP
        LHLD    KOO
        CALL    POSIT
        JMP     PLUS

KOO:    DW      0

HORIZ:  CALL    POSIT
        MVI     B,22
        MVI     C,'-'
HOR02:  PUSH    B
        CALL    CNOUT
        POP     B
        DCR     B
        JNZ     HOR02
        RET

PLUS:   MVI     C,'+'
        JMP     CNOUT

POSIT:  PUSH    H
        MVI     C,27
        CALL    CNOUT
        MVI     C,59H
        CALL    CNOUT
        POP     H
        PUSH    H
        MOV     A,L
        ADI     32
        MOV     C,A
        CALL    CNOUT
        POP     H
        MOV     A,H
        ADI     32
        MOV     C,A
CNOUT:
        DB      0C3H
BIO4C:  DW      0

        endfunc

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

        function F82A

        CALL    MA1TOH
        SHLD    PRM1
        CALL    MA2TOH
        PUSH    B
        MOV     D,H
        MOV     E,L
        DB      21H
PRM1:   DW      0
        CALL    0F82AH
        MOV     H,B
        MOV     L,C
        POP     B
        RET

        endfunc


Обратите внимание, что функция MSSG (аналог п/п F818 в РК) использует вывод на экран через вход CONOUT в CP/M-BIOS. А т.к в разных CP/M (на разных машинах) адрес BIOS - разный, то сначала этот адрес CONOUT подпрограммой MSSG высчитывается и подставляется методом модификации кода. Так делают все грамотные программы CP/M. Ещё там есть функция рисующая на экране символами две панели нортона и также видно как просто ассемблерной функцией вызывать п/п-ммы ROM-BIOS компьютера (в данном случае вызывается подсчёт КС, вероятно это было надо для занесения КС файла в метку ORDOS-файлов).

А вот как панели нортона рисуются самим Си (тут похоже на Си делается то же самое, что делает ассеблерная функция PANEL). Эта функция, очевидно, была в теле основного модуля, но когда выяснилось, что это тормознуто, то было заменено на ассемблерную (но ключом NOASM тут можно выбрать - использовать эту подпрограмму на Си или на ассемблере).

отрисовка панелей кодом на Си:

rames()
{
rama(lp_ofs); rama(rp_ofs);
gotoxy(0,lp_ofs+7); printf(" %c:\\User %d ",'@'+disk1,user1);
gotoxy(0,rp_ofs+7); printf(" %c:\\User %d ",'@'+disk2,user2);
}

rama(p_ofs)
int p_ofs;
{
int i;
gotoxy(0,p_ofs); bios(4,'+');
for (i=0;i<22;i++) bios(4,'-'); bios(4,'+');
for (i=1;i<VSIZE-1;i++) { gotoxy(i,p_ofs+23); bios(4,'|'); }
gotoxy(VSIZE-1,p_ofs); bios(4,'+');
for (i=0;i<22;i++) bios(4,'-'); bios(4,'+');
for (i=VSIZE-2;i>0;i--) { gotoxy(i,p_ofs); bios(4,'|'); }

/* for (i=1; i<VSIZE-1; i++) { gotoxy(i,p_ofs+23); bios(4,'|'); } */

gotoxy(0,p_ofs+15); bios(4,'+');
for (i=1; i<VSIZE-1; i++) { gotoxy(i,p_ofs+15); bios(4,'|'); }
gotoxy(VSIZE-1,p_ofs+15); bios(4,'+');
}

Ассемблерные функции разумно поместить в один файл с расширением CSM, который затем транслируется с помощью программы CASM.COM (это компилятор с ассемблера в REL-формат принятый в BDS), на выходе получается странслированный файл с расширением CRL (что очевидно акроним от C и REL). Си-модули компилируются компилятором BDS-C (это CC.COM) также в формат CRL. Ну а далее, все оттранслированные CRL-модули линкуются вместе компоновщиком BDS.

Жаль, что BDS не берёт стандартный REL-формат, тогда бы ассемблерные куски можно было бы писать в нормальной мнемонике Z80. Но надо ещё почитать доки, может это всё-же можно (например я встречал компиляторы Паскаля и PL/M, которые могут использовать как свой REL-формат, так и майкрософтовский).

Вот здесь можно скачать BDS-C вместе с доками и примерами.

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

При трезвой оценке и из некоторого опыта применения компиляторов Си и Паскаля для 8-ми разрядок можно сделать вывод, что минимальные требования к железу при разработке программ на этих ЯВУ - это Z80 с реальным клоком от 5 МГЦ и CP/M с TPA от 62 кб (именно TPA - Transient Programm Area, а не free contiguous RAM). Тогда более-менее серъёзные программы из под ЯВУ будут умещаться в доступное ОЗУ и не особо тормозить. Да и при 5 МГЦ такта при написании динамичных игр останется резон критичные процедуры всё-равно писать на ассемблере, хотя благодаря изобилию ОЗУ исчезнет смысл (ради сжатия кода) всё, что можно, переписывать на ассемблер, т.к ОЗУ будет хватать. А это заметно ускорит программирование.

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

И всё же мне известно, что в России произошло несколько случаев более-менее успешного написания игр для 8-ми разрядки на Си. Это использование убогого самодельного BEST C для написания нескольких графических игр для ОРИОНА и профессиональный программист В.Широков из Воронежа в 1992 году написал две игры (Xeno World и Ranger) для ОРИОНА используя именно BDS-C и граф.библиотеку, по функциям почти идентичную граф.функциям из Турбо Си MSDOS (эта библиотека есть в архиве по вышеприведённой ссылке). Возможно движения спрайтов в этих играх чуть скачкообразнее, чем в играх на ассемблере, но всё-же визуально эти игры не хуже тех, что написаны на ассемблере.

Если уж у графического ОРИОНА (только не помню в цвете или без) его мизерного такта в 2.5 МГЦ хватило, то это позволяет нам вполне обоснованно надеяться, что не всё настолько плохо с быстродействием кода из под Си и других ЯВУ и для менее требовательной к скорости CPU текстовой машины скоростей ЯВУ хватит.

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

BDS C характерен удобством при использовании в Си-программах вкраплений кода написанных на ассемблере. К сожалению писать эти куски требуется в мнемонике КР580, т.к ассемблер компилятора Z80-мнемоники не понимает. Передача папраметров в ассемблерные процедуры и функции также очень удобна, т.к Леор Золман предусмотрел в RUN-тайм модуле удобные подпрограммы для извлечения параметров из стека.

Т.к речь идёт в первую очередь о написании на BDS C игр для РК86, что предназначены для работы без CP/M, а компилятор рассчитан на генерацию кода для работы из CP/M, то на ассемблере требуется написать в первую очередь процедуры вывода на экран и функции опроса клавиатуры. Это аналоги стандартных п/п-мм ПЗУ РК86 - F809, F815, F818, F81B, F812 и F803. Сам Си не может вызывать ассемблерные подпрограммы, потому на ассемблере надо написать внешние процедуры выполняюще это.

В качестве альтернативы переадресации вызовов на ROM-BIOS можно использовать блок эмуляции консольных функций CP/M, который должен загружаться в ОЗУ при старте самой программой и как бы создавать на машине среду CP/M (но без дисковых функций). Это, естественно, более грамотный подход позволяющий использовать стандартаные в CP/M функции ввода/вывода с форматированием (т.е с вводом/выводом в заданном формате, например, в HEX-виде, в символьном виде, в числовом виде с разной точностью и т.п).

На скорую руку, чтобы хоть что-то попробовать написать на Си я нацарапал свой простенький набор ассемблерных функций для связи с эр-кашным ROM-BIOS в ПЗУ, странслировал в один CRL-модуль. А затем с целью проверить саму работоспособность компилятора BDS C написал простенькие тесты использующие эти вызовы.

процедуры для вызова РК-ПЗУ:

       INCLUDE   <BDS.LIB>

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

        function MSSG

        CALL    MA1TOH
MSGLOO: MOV     A,M
        ORA     A
        RZ
        PUSH    B
        MOV     C,A
        CALL    0F809H
        POP     B
        INX     H
        JMP     MSGLOO

        endfunc

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

        function F81B

        CALL    0F81BH
        MVI     H,0
        MOV     L,A
        RET

        endfunc

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

        function POS_XY         /* первый параметр V, второй H */

POS_XY:
        CALL    MA1TOH
        STA     PARM1
        CALL    MA2TOH
        PUSH    B

        MVI     C,1BH
        CALL    0F809H
        MVI     C,59H
        CALL    0F809H

        LD      A,L
        ADI     32
        MOV     C,A
        CALL    0F809H

        DB      3EH
PARM1:  DS      1
        ADI     32
        MOV     C,A
        CALL    0F809H
        POP     B
        RET
       
        endfunc

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

        function F82A

        CALL    MA1TOH
        SHLD    PRM1
        CALL    MA2TOH
        PUSH    B
        MOV     D,H
        MOV     E,L
        DB      21H
PRM1:   DS      2
        CALL    0F82AH
        MOV     H,B
        MOV     L,C
        POP     B
        RET

        endfunc

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

        function F809

        CALL    MA1TOH
        PUSH    B
        MOV     C,A
        CALL    0F809H
        POP     B
        RET

        endfunc

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

        function F815

        CALL    MA1TOH
        JMP     0F815H

        endfunc

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

        function F803

        CALL    0F803H
        LD      L,A
        LD      H,0
        RET

        endfunc
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty для КР580 есть системные программы и игры написанные на BDS C

Сообщение  barsik Вт Дек 08 2020, 09:02

2
Изучая разные материалы про BDS C узнал, что оказывается самый крутой 8-ми разрядный текстов редактор Final Word для CP/M был написан на Си. Не знаю на каком именно Си, но он компилировался и на BDS C. Final Word в 1986 русифицировали в Омске (русифицировали и конфигурировали для большего удобства русскоязычных, изменили имена файлов, вряд-ли меняли сам код, т.к для этого надо быть очень умным) и назвали получившийся редактор SuperText. Это значит, что Си не настолько уж бесполезен для 8-ми разрядок, раз на нём писали даже настолько великолепные программы.

Нашёл исходники нескольких игр написанных на BDS C, в том числе OTHELLO, RALLY, LIFE и TIC TAC TOE. А ещё нашёл исходник ПЭКМАНА и узнал, что он был написан на Си и компилируется в BDS C. Насчёт других игр пока ничего не знаю, зато знаю, что пэкман это вполне динамичная игра, что чётко подтверждает мою мысль, что для текстовой машины для написания игр можно использовать ЯВУ. Исходник этого пэкмана есть, так что разобравшись и частично переделав, можно попробовать сделать версию для РК86, а может быть даже и для ИРИШИ. Кстати, текстовые сообщения в этом пэкмане на немецком языке (что делает праздник на улице англохэйтеров и германофилов).

К тому же удачно, что этот пэкман под экран шириной всего в 64 символа (а во всех остальных играх для CP/M, что я видел ранее, ширина экрана - 80). Для РК ширина 80 это критично, а вот для ИРИШИ - нет, т.к она как раз даёт 80 символов в строке в видео режиме 2.

Но зато в ИРИШЕ максимум это всего 25 строк и, если бы в пэкмане потребовалось 32 строки, то были бы большие проблемы (по счастью, в этом пэкмане достаточно 25-ти строк, - я это вывел из того, что в операторе SPLOT максимальная цифра для V-позиции - 24). Кстати SPLOT это вывод текста с заданной экранной позиции - у неё в параметрах сам текст и X,Y координаты куда его выводить (естественно заимствую эту удобную процедуру для своих эр-кашных Си-игр ).

У ИРИШИ с отображением большого числа строк трудности. Т.к у неё всего 200 линий в растре. И если написать драйвер на 32 строки, а 200 линий растра разделить на 32 строки это будет (калькулятор в Windows всегда под рукой) - 6.25 линий на строку. Для букв и цифр знакоместо высотой в 6 линий маловато (т.к хотя бы одна линия должна разделять строки, а высотой всего в 5 линий понятный фонт не сделать), а вот для игры это не критично (всё-равно там монстры и сам пэкман изображаются буковками).

Даже, если на ИРИШЕ использовать фонт высотой в 7 линий растра, то можно будет написать и поиметь драйвер дающий всего лишь 28 строк (т.к 200:7= 28.5). А у РК86, как назло, все игры в псевдографике нагло используют режим в 30 строк, так что их адаптировать в лоб на ИРИШУ не получится, - придётся переделывать логику под экран ИРИШИ с V-размером всего в 28 строк.

Но пока проблема с этим пэкманом даже вовсе не в числе строк и числе символов в строке. Попробовал странслировать для ОРИОНА и, увы, ничерта не работает. Думаю, что проблема в том, что этот код кидает какие-то левые байты в какие-то I/O порты, а ОРИОНЕ такое дерьмо попадает в ОЗУ (возможно это инициализация видеорежима в конкретной CP/M машины).

Надо разбираться в исходнике (а это долго и не так-то просто для малоопытного) и для отладки даже возможно пользоваться Си-отладчиком BDS-пакета, чтобы узнать где виснет. Пока опыта сразу во всём разобраться нет, но уже хорошо, что есть хотя бы одна динамичная игра на Си и можно её изучать и заимствовать что-то. Буду постепенно изучать этот исходник, пока не разберусь во всём. И надо бы поискать в Интеренет ещё игры написанные на чистом Си для текстовых машин. Ещё хотя бы несколько текстовых игр на Си скорее всего можно отыскать в исходниках (иначе чем же занимались американские любители купившие BDS C в начале 80-тых).

Было бы любопытно посмотреть с какой скоростью на тормозной оригинальной ИРИШЕ получится игра. Из скоростных соображений лучше для игры использовать граф.режим 1 с графикой 320*200 моно, т.к это самый скоростной видео режим ИРИШИ (не считая, понятно, скоростного режима с погашенным экраном, когда ИРИША раскочегаривается на полные 1.77 МГЦ, вот это уже приличная скорость ! - жаль лишь, что на экране при этом сплошная чернота, но зато есть простор для воображения, а возможно кому-то играть в игры "в слепую" - даже интереснее).

И всё-равно хотя у ИРИШИ скорость в видео режиме 1 аж ~1.5 МГЦ, что быстрее, чем жалкие 1.3 МГЦ у РК86, но зато РК - текстовый и скорее всего будет, как минимум, одинаков по скорости с ИРИШЕЙ. Ну а  в высокоразрешающем режиме 640*200 у ИРИШИ быстродействие падает до ~1 МГЦ, и тут даже нетурбированный базовый РК86 явно будет работать раза в два, а то и в три, быстрее. Можете сами попробовать разобраться почему не работает этот пэкман.

Кроме BDC-C для КР580 годится ещё компилятор AZTEC-C, который не только может генерить код для КР580, но также и для Z80. И, кажется, на нём можно писать ассемблерные фрагменты в мнемонике Z80, т.к у него REL-формат совместим с Microsoft M80. Но пока не стану заморачиваться с ацтеком (т.к жалко времени, что уже потратил на BDS C), по всему выходит, что и BDS C неплох.

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

Пока у меня не получилось с ходу пройти даже подготовительный этап освоения BDS C, т.е получить среду программирования и начать. Проблема в компиляторе. Что он для CP/M. Мне требуется заставить его компилировать не для среды CP/M, а для среды монитора РК86. Но, чтобы по хорошему этого добиться надо переделать, переписать, а затем перетранслировать несколько ассемблерных файлов RUN-тайм модуля и 4-х стандартных библиотек и все адреса должны совпадать (а их исходники написаны в какой-то левой мнемонике, которую я воспринимаю с торможением, т.е приходится напрягать мозг, а он у меня и без того уже сильно изношенный и уже в или близок к Альцгеймеру).

В общем, пока мне не удалось решить эту задачу и поиметь пригодный инструментарий. И даже имитатор BDOS функций с ходу не получился. Для РК86 у которого есть среда CP/M, я могу странслировать Си-программы, а для мониторной среды, пока не могу. Даже "Hello World !" не могу. Т.е пока не имею отработанной технологии, которая позволяла бы уже начать переписывать старые РК-игры на Си.

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

Но зато есть и хорошие новости. Выяснил, что мне удастся транслируя компилятором BDS C для КР580, тем не менее использовать для ассемблерных фрагментов нормальную мнемонику (кстати, AZTEC это позволяет, т.к с ним можно транслировать с помощью M80, который понимает нормальные мнемоники, но теперь AZTEC меня уже не интересует).

Такой вывод я сделал после прочтения инфо про формат CRL. Оказывается это не REL-формат в обычном смысле, а обычный COM-файл, который получается загрузкой HEX-файла в отладчик и записью загруженного COM-кода в CP/M-файл. Которому однако присваивается не расширение COM, а CRL. Но по сути это просто COM-файл оттранслированный для работы с адреса 100. А как мы получаем такой COM-файл, - линковщик не волнует. Это даёт возможность странслировать исходник ассемблерных модулей Си-программы используя M80, который понимает мнемоники Z80.

Теперь я могу писать в нормальной мнемонике ASM-исходник соблюдая соглашения для CRL-файлов (в них список имён подпрограмм и их адресов группируется в начале кода по своим особым правилам). И я вспомнил, что в 90-е годы так и использовал BDS, не используя CASM, что позволяет иметь исходники в нормальной мнемонике. К сожалению исходники на BDS C из 90-тых сейчас недоступны (лишь нашёл какой-то левый исходник, но в нём я ещё использовал CASM, который не понимает мнемоники Z80 и требует мнемоник КР580). Но это не беда, сейчас другие задачи, да и разбираться в незнакомых (пусть и своих) исходниках неинтересно, главное теперь я знаю всё, что мне надо знать.

Так, что теперь уже нет смысла переключаться на AZTEC C, о чём я уже начал было задумываться, т.к с трудом переношу неправильные мнемоники ассемблера. К тому же отпадает один этап в трансляции (обработка исходника CASM-ом), что сокращает потерю времени на одну итерацию.

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

Ну вот я наконец пробился сквозь стартовые трудности. Т.е странслировал компилятором исходно расчитанным на CP/M программу Hello World работающую на РК86 в среде монитора. Путь и не так как исходно пытался, а упрощённо и тупо с эмуляцией BDOS, но это тоже имеет свои плюсы. Т.к думаю, что сумею сделать более простую (без манипуляций отладчиком), т.е автоматическую инсталляцию среды. Будет достаточно только прилинковывать к Си-программе INIT-модуль и первой командой в программе надо вызывать процедуру INIT(). Но это никак не нагружает программиста, т.к транслирует и линкует BAT-файл.

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

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

Время на итерацию также сократил где-то до 45 секунд, транслируя в Windows, а не в эмуляторе ОРИОНА. Это быстрее, хотя из-за того, что приходится делать много манипуляций и выполнять ввод с клавиатуры в CP/M-отладчике работающим под TSR-эмулятором MSDOS приходится выгружать яндексовый Punto Switcher. Т.к он перехватывает INT 9, то конфликтует с TSR и приходится Punto выгружать, иначе он тормозит ввод, что меня нервирует. Кстати и все мои эмуляторы (как и большинство эмуляторов MSDOS) перехватывают INT 9, потому из них не запускается скриншот и перед их запуском приходит также выгружать Punto.

Теперь осталось сделать среду РК в эмуляторе.  Это недолго, найти исходники своего эмулятора РК на ОРИОНЕ и странслировать версию пока для базового видео режима 128*60, т.к первыми играми я хочу попробовать делать аналоги старых РК-шных игр (которые используют такой режим), чтобы можно было сравнить. Затем попробую режим в 43 строки с графикой 192*86, т.к такая версия уже есть. А для режима 192*104 версии эмулятора пока нет, т.к я не знал о таком режиме в 90-тые годы (это режим на 60 ГЦ, а всем известно, что в телевидении стандарт частоты кадров 50 ГЦ)



Также за вчера нашёл и проинсталлировал Турбо-Си 3.0 и Borland Си 3.1. Последний внешне почти то же, но по содержимому мощнее, - транслирует не только для MSDOS, но также и для Windows (видимо для Windows 3.1, но это без разницы, т.к API одинаков). Меня интересует только возможность транслировать в консольное приложение для Windows. Это позволит получив исходник текстовой игры для РК странслировать её версию и для Windows. Вроде бы можно в консольном приложении задать много строк, но всё-равно это смогут быть только чисто текстовые РК-игры, не псевдографические, т.к вряд-ли там можно грузить свой фонт.

В этих версиях Си из 1992 года уже работает мыша (в отличие от TC 2.0 из 1987 года, потому решил с ним больше не связываться). Пока буду использовать эти IDE лишь в качестве Хэлпа, т.к он там встроенный и удобный - кликаешь на Си-операторе и получешь по нему описание.

С Турбо-Си удобно, что здесь не бывает RUN-time ошибки 200, которая возникает в программе на TP, если машина скоростнее, чем 486-тая. На 386-й и 486-й у меня программы из под TP работали, а когда поимел Пентиум, то стала возникать ошибка 200. Из-за этого и пришлось начать учить Delphi и CBuilder.

В XXI-веке решили как бороться с ошибкой 200, достаточно заменить один юнит CRT.TPU. Это поможет, если сохранён исходник. А вот если остался только код программы странслированной на TP 7.1, то её можно запускать лишь из под DOS-BOX, который тормозит прогон до скорости 486-той, но это не самое удобное решение этой проблемы ERROR 200.

Но по счастью на TC 2.0 и 3.0 проблем с ERROR 200 нет. Видимо потому, что TC появился на несколько лет позднее, когда уже программисты разработчики сообразили, что считать время самим процессором глупо, т.к машины быстро совершенствуются и ускоряются.

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

Как я и предполагал в предыдущем посте, используя знания об устройстве CRL-объектных файлов (что любезно включил в доки по BDS C компилятору Leor Zolman), мне удалось решить проблему ущербности мнемоник ретро компилятора. Т.е теперь я могу делать ассемблерные процедуры в мнемониках процессоров Z80 и 6502 (т.к исходники для 6502 компилятор М80 тоже транслирует). Это было со стороны автора именно любезно, т.к используя компилятор стандартно предложенным способом (т.е с помощью конвертора исходников в нужную структуру по имени CASM.COM), эти сведения ни за чем не нужны.

Т.к файлы в нужном формате создаёт препроцессор CASM, избавляя программиста от нюансов. Однако, людей программирующих уже после 1979 года (когда был написан компилятор) уже не устраивает использованная автором компилятора допотопная мнемоника, что имела ещё какое-то ограниченное хождение в конце 70-тых годов.

И оказалось, что если самостоятельно писать процедуры на ассемблере вызываемые из Си строго соблюдая требуемые для Си-компилятора соглашения по структуре модулей, то можно использовать компилятор макро-ассемблера M80 от Microsoft и писать в понятных людям мнемониках Z80.

Это конечно утомительно и чревато дополнительными ошибками (и последующим долгим их поиском) и сложнее, чем пользоваться простым оформлением ASM-процедур в виде простого шаблона function... endfunc и последующим casm-ированием файлов для приведения исходников в формат CRL-модулей. Интересно, что такая методика возможна только используя M80 от Microsoft, а примитивные самопальные любительские Z80-ассемблеры для Windows для этого не годятся.

На самом деле я "изобрёл велосипед", ибо до этой же идеи додумались все отечественные программисты, что использовали BDS C в 80-тые и 90-тые годы. Ибо среди них не было людей, кому доставляло бы удовольствие трахаться с "ничего неговорящими" мнемониками КР580. Потому именно все программисты, чтобы иметь мнемоники Z80 использовали такое, даже не особо утомительное, составление ASM-процедур в CRL-формате (я сам в том числе делал так же, но спустя всего какие-то 20 лет совсем забыл об этом).

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

Спойлер:

       .Z80
        aseg
        ORG     100H

YF803   EQU     0F803H
YF809   EQU     0F809H
YF812   EQU     0F812H
YF815   EQU     0F815H
YF81B   EQU     0F81BH
YF821   EQU     0F821H
YF82A   EQU     0F82AH

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

        defb    'COU','T' OR 128
        DW      COUT- 100H

        defb    'MSS','G' OR 128
        DW      MSSG- 100H

        defb    'PAUS','E' OR 128
        DW      PAUSE- 100H

        defb    'WAIFR','E' OR 128
        DW      WAIFRE- 100H

        defb    'POSI','T' OR 128
        DW      POSIT- 100H

        defb    'F81','B' OR 128
        DW      F81B- 100H

        defb    'F80','3' OR 128
        DW      F803- 100H

        defb    'F81','5' OR 128
        DW      F815- 100H

        defb    'F82','1' OR 128
        DW      F821- 100H

        defb    'F82','A' OR 128
        DW      F82A- 100H

        defb    'WAIKEY' OR 128
        DW      WAIKEY- 100H

        defb    'INIRS'',T' OR 128
        DW      INIRST- 100H

        defb    80H
        DW      MODLEN

        aseg
        ORG     305H

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

COUT:   'MA1TO','H' OR 128
        defb    0
        DW      EE01-BB01

        .phase  0
BB01:
        JP      6
        JP      0

        CALL    MA1TOH
SCOUTA: PUSH    BC
        LD      C,A
        CALL    YF809
        POP     BC
        RET
EE01:
        .dephase

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

        .phase  0
       
PAUSE:  'MA1TO','H' OR 128
        defb    0
        DW      EE03-BB03

        .phase  0
BB03:
        JP      6
        JP      0

        CALL    MA1TOH
        LD      H,L
        LD      L,0
LOO1:   DEC     HL
        LD      A,H
        OR      L
        JP      NZ,LOO1
        RET
EE03:
        .dephase
       
; ----------------------------------------------


WAIFRE: defb    0
        DW      EE04-BB04

        .phase  0
BB04:
        CALL    YF81B
        INC     A
        JP      NZ,BB13
JJ_100: DEC     A
        JP      NZ,JJ_100
        CALL    YF81B
        INC     A
        JP      NZ,BB04
        RET
EE04:
        .dephase

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

MSSG:   'MA1TO','H' OR 128
        defb    0
        DW      EE05-BB05

        .phase  0
BB05:
        JP      6
        JP      0

        CALL    MA1TOH
MSGLOO: LD      A,(HL)
        OR      A
        RET     Z
        CALL    SCOUTA
        INC     HL
        JP      MSGLOO
EE05:

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

F81B:   defb    0
        DW      EE06-BB06

        .phase  0
BB06:
        CALL    YF81B
        LD      H,0
        LD      L,A
        RET
EE06:

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

POSIT:  'MA1TO','H' OR 128
        'MA2TO','H' OR 128
        defb    0
        DW      EE07-BB07

        .phase  0
BB07:
        JP      9
        JP      0
        JP      0

POSXY:  CALL    MA1TOH          ; первый параметр V, второй H
        PUSH    AF
        CALL    MA2TOH
        LD      A,1BH
        CALL    SCOUTA
        LD      A,59H
        CALL    SCOUTA
        LD      A,L
        ADI     32
        CALL    SCOUTA
        POP     AF       ; это возможно лишь потому, что BC не портим        
        ADD     A,32
        JP      SCOUTA
EE07:
       
; ----------------------------------------------

F82A:   'MA1TO','H' OR 128
        'MA2TO','H' OR 128
        defb    0
        DW      EE08-BB08

        .phase  0
BB08:
        JP      9
        JP      0
        JP      0

        CALL    MA1TOH
        LD      (PARM1),HL
        CALL    MA2TOH
        PUSH    BC
        LD      D,H
        LD      E,L
        defb    21H
PARM1:  DS      2
        CALL    YF82A
        LD      H,B
        LD      L,C
        POP     B
        RET
EE08:

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

F821:   defb    0
        DW      EE09-BB09

        .phase  0
BB09:
        CALL    YF821
        LD      L,A
        LD      H,0
        RET
EE09:
        .dephase
       
; ----------------------------------------------

F815:   'MA1TO','H' OR 128
        defb    0
        DW      EE10-BB10

        .phase  0
BB10:
        JP      6
        JP      0

        CALL    MA1TOH
        JP      YF815
EE10:
        .dephase

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

F803:   defb    0
        DW      EE11-BB11

        .phase  0
BB11:
        CALL    YF803
        LD      L,A
        LD      H,0
        RET
EE11:
        .dephase

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

WAIKEY: defb    0
        DW      EE12-BB12

        .phase  0
BB12:
        JP    YF803
EE12:
        .dephase

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

MODLEN  EQU     $-100H
       
        END

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

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

Стоит упомянуть , что есть ещё несколько Си-компиляторов, которые могут генерить код для процессора КР580. Однако они любительские и по-видимому не являются серъёзными конкурентами для таких серъёзных и успешных коммерческих продуктов, как AZTEC и BDS Си (и по-видимому они могут рассматриваться как Си-инструмент лишь при полном безрыбье). Большинство из них получены доработками (и иногда даже весьма значительными по объёму) из журнального Си компилятора опубликованного в журнале BYTE (где-то в начале 80-тых годов), хотя встречаются и оригинальные проекты и даже бывшие в начале 80-тых коммерческими (например, Manx C и Whitesmith С). Я все такие компиляторы (т.е подозреваемые на возможность давать код КР580) скачал, но ещё не разбирался ни с одним из них.

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

BDS C был первым ранним продуктом на рынке потому поимел относительный коммерческий успех (хотя разработчика Л.Золмана обжулила торгующая его компилятором фирма, и если бы не её эгоизм, то объём продаж был бы со слов Леора Золмана раза в 3 большим). Если BDS C имел успех лишь в первый период в 1979...1982, а после автор Леор Золман прекратил работы над ним и его сопровождение (хотя судя по докам BDS C продавался до 1988), то его конкурента компилятор AZTEC C разрабатывала фирма из 4-х человек и они относились к своему проекту намного серъёзнее и продержались на рынке намного дольше (даже перешли с середины 80-тых на процессор 8086 и MSDOS, чего BDS не сделал,  т.к к тому времени уже выбыл из рынка).

Так фирма AZTEC уже к 1984 сделала версии своего компилятора для всех 8-ми разрядных пратформ и не только для CP/M и процессоров 8080/Z80, но и для 8086, 6502 и других CPU и ОС. Были выпущены и продавались версии и для CP/M-86 и для MSDOS и эта фирма продавала свои компиляторы непрерывно более 15 лет. Также в 1988 году ими были выпущены кросс-компиляторы для MSDOS позволяющие разработку для 8-ми разрядок в ОС MSDOS на IBM PC XT (и в том числе даже для CPU 6502, хотя он в силу отстутствия 16-ти битовых регистров менее выгоден для ЯВУ, где параметры процедур и функций всегда 16-ти битовые).

Потому любопытно было бы странслировать одну и ту же Си-программу на 2000 строк на обоих компиляторах (AZTEC и BDS) и сравнить результат. Я решил сейчас попробовать осваивать BDS лишь потому, что уже имел с ним дело ранее (в середине 90-тых), что в свою очередь произошло потому, что BDS C в начале 90-тых использовали знакомые мне программисты для ОРИОНА. Но в других городах люди выбрали и использовали AZTEC и тоже добились больших успехов с его помощью. Так что вопрос о лучшем Си компилятора для РК86 пока не решён. Но пока я, естественно, метаться не буду, т.к уже потратил более десятка часов на освоение BDS и пока меня всё в нём устраивает (кроме слабой диагностики ошибок).
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty .

Сообщение  barsik Вт Дек 08 2020, 11:01

3
Отличная новость для нечокнутых ретро программистов (нечокнутые это те, кто используют в ассемблерных исходниках нормальные мнемоники вместо допотопных). Сканируя Интернет в поисках любой полезной информации про компиляторы Си для CP/M скачал выложенный самим Леором Золманом последний пакет обновлений из 2002 года (BDS C Compiler/Linker Retail Distributions and Source Code).

Там оказались не только исходники всех программ пакета компилятора версии 1.60 (что есть последняя версия, что делал сам Леор Золман, т.к версию 2.0 делал уже не он), но и дистрибутив BDS C 1.60 для процессора 8080 из 1986 года. А ранее я использовал и использую сейчас (а точнее на этой неделе) более древнюю версию 1.50A из 1982 года, что единственная имела хождение в СССР, т.к советские разведчики резиденты (т.е шпионы занимающиеся промышленным шпионажем) достали лишь её.

Видимо к 1986 году у наших шпионов задачи воровать инструментальное ПО для ОС CP/M уже не стояло, актуальнее стала задача воровать ПО для MSDOS. Из-за чего всё CP/M ПО, что имелось в СССР, имело дату выпуска не старше 1982 года (вот из-за таких фатальных ошибок ГРУ мы не смогли решить задачу "догнать и перегнать США", чётко поставленную перед советским народом товарищем Н.С.Хрущёвым).

Так вот в дистрибутиве этой версии BDS C 1.60 оказался включённым пакет сторонних разработчиков фирмы XEROX (из 1984 года) содержащий CASM для мнемоник процессора Z80, который конвертирует исходники в формат пригодный для M80/L80 от Microsoft. Как видим нормальных людей уже в начале 80-тых годов предельно утомил этот "лохматый ужас на крыльях ночи", т.е кошмарные мнемоники допотопного процессора и пользуясь тем, что Leor Zolman любезно включил в дистрибутив (в качестве примера использования BDS C) и исходник CASM для 8080 написанный на Си, сумели переделать его под мнемоники более передового процессора Z80. Что в очередной раз доказывает, что только сумасшедшие, имея возможность выбора, пользуются допотопными мнемониками Intel.

Это шикарная новость. Теперь нормальным людям использующим мнемонику Z80 уже не требуется вручную моделировать структуру CRL-файла с постоянным риском что-то перепутать с адресацией или размерами и получить неразрешимую головную боль на десятки часов электротраха. Достаточно просто написать, как обычно подпрограмму на ассемблере Z80 и обрамить её тэгами FUNCTION... ENDFUNC и на этом все заботы программиста о подстыковке ассемблерных фрагментов в Си-программу заканчиваются.

Более того, в эту же выкладку от Леора Золмана оказался включённым и BDS C версии 2.0 из 1989 года для Z80. Этот пакет готовил уже не Леор Золман, а другой человек - какой-то любитель, которому автор Леор Золман доверил сопровождение и поддержку своего компилятора в конце 1980-тых годов, когда это потеряло всякий коммерческий смысл. В этот пакет включён уже другой (универсальный для Z80/8080) CASM (названный CAZM) написанный другим человеком в конце 1980-тых годов и доработанный в 1991 году.

Все стандартные C-функции написанные на ассемблере в этой Z80-версии компилятора переписаны на ассемблер (и соответственно мнемоники) Z80. Разумеется это (т.к алгоритм и использование регистров не меняются), если и даст ускорение, то лишь всего на 5% (благодаря более коротким двухбайтовым JR-командам). А вот код самого компилятора скорее всего оставлен исходным (без использования команд Z80) и эффективность самого выходного кода вряд-ли улучшилась (ибо исходника-то у этого человека не было, он опубликован лишь в 2002 году, да и разобраться в чужом коде вряд-ли было бы легко).

Пока мне не надо производить код для процессора Z80, а надо лишь использовать его мнемоники программируя для процессора КР580, потому я даже к версии 1.60 пока не перейду. Ибо кто её знает... а вот версией 1.50 все кто её использовали в СССР и в России были вполне довольны и даже визжали от восторга.

Лишь заимствую ZCASM из дистрибутива 1.60 (т.е даже не более свежий CAZM из дистрибутива BDS C 2.0 из 1989). Этого ZCASM из 1984 года будет вполне достаточно. Ведь CASM не меняет мнемоники, а лишь вставляет в текст доп.строки дающие нужную структуру CRL-файла, потому без разницы насколько он крут.

Любопытно, что в READ.ME файле Леор Золман признаёт, что его BDS C уступает в скорости кода Whitesmith C из-за того, что он принял bad decision использовать пару BC для хранения адреса стека с параметрами на нём, что, или лишало возможности использовать BC в функциях и процедурах, или требовало ставить лишние PUSH-POP, что также снижало скорость.

Вот начало ДОК-файла про ZCASM:

Спойлер:
       Copyright (c) 1984 Brian Waldron, Xerox Corp. based on CASM by Leor Zolman

This document describes the ZCASM Assembly language --> CRL-Format Preprocessor for BDS C 1.5. The original program, CASM.C, has been modified to process ZILOG mnemonics and output short labels for assembly with Microsoft's M80/L80.

The files making up the ZCASM package are as follows:

ZCASM.DOC - this file
ZCASM.C - source file for ZCASM program
ZCASM.SUB - submit file for performing conversion of CZM file to CRL

Which looks like this:

       ZCASM $1
       M80 =$1/Z
       L80 $1,$1.CRL/N/E


А вот начало документации к более позднему по времени конвертору исходников в CRL-формат по имени CAZM. Этот чувак похоже написал какой-то свой левый CLRMAC Z80 макро-ассемблер и потому ломит под него: "SLR products are strongly recommended... and almost unbelievably fast". Как будто стандартного микрософтовского M80/L80 ему мало. А скорее всего он его просто не купил (ибо он стоил $300, что не каждому по карману), потому и использовать не мог.

В пропаганду своего ассемблера автор CAZM несёт полную пургу: "users might like a more generalized program for fast, flexible, modern assemblers such as SLR Systems' SLRMAC and Z80ASM." SLRMAC я не имел и не знаком с ним, но Z80ASM знают все и это полнейший примитив, слабее которого нет ничего. Его даже сравнивать с M80 нелепо и даже ставить рядом нельзя. А насколько его собственный SLRMAC ассемблер убогий видно уже по необходимости для конверсии в COM-файл дополнительно использовать ещё и CLOAD), хотя, видимо, его преимущество в скорости трансляции и возможности использовать имена и метки длиной до 14 символов (вместо 6 для Microsoft M80).

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

Спойлер:
       Preliminary Documentation for CAZM v0.3 (beta release)
       a Z80 Assembly_Language_to_CRL_Format Preprocessor for BDS C vZ2.0
       Jim Chapin, March, 1992


          Introduction

The CASM preprocessor is provided with the BDS C Compiler package to convert specially prepared "CSM-format" source files for assembly by DRI's ASM or MAC. Another BDS C program, CLOAD, is used to convert the resulting HEX file to the CRL format that is recognized by the BDS C linkers, CLINK and L2. The process is quick, and requires very little modification of working ASM format
routines.

Unfortunately for Z80 users, CASM was designed for the Intel 8080 mnemonics and limited pseudo-ops supported by ASM and MAC. This reduces the appeal of the assembly-to-CRL translation system for programmers accustomed to an advanced Z80 assembler.

ZCASM is a modification of the basic CASM program that provides preprocessing for the once-popular M80 assembler in Z80 mode.  ZCASM has some limitations of its own, mostly because of the assembler it was designed for. The latest version I know of, v1.5, was written for BDS C v1.50a.

After comparing CASM v1.6 and ZCASM v1.5, it seemed to me that BDS C vZ2.0 users might like a more generalized program for fast, flexible, modern assemblers such as SLR Systems' SLRMAC and Z80ASM. The result is CAZM.

CAZM can be quickly configured for either Zilog or extended Intel (Z80.LIB) mnemonics.  It supports the full Z80 instruction set in either configuration. When CAZM is compiled for Intel mnemonics, the distribution CSM files can be used without alteration.  The intent was NOT to make existing, reliable code obsolete.

CAZM is used the same way as CASM. BDS.LIB can be used without serious alteration (see point below concerning data definition pseudo-ops). A typical conversion might go like this for file FOOBAR.CSM:

       cazm foobar             ;; BDS C/assembly code --> normal assembly source
       slrmac foobar/h         ;; assembly source --> HEX file
       cload foobar            ;; conversion HEX file --> to CRL file

If you do not have one of the SLR assemblers, CAZM may still work as written if your assembler understands the DEFC or DC pseudo-op (see point below), the ELSE pseudo-op, and can distinguish between long labels (14 characters or so). If your assembler produces only REL-file output, you may have problems with link restrictions on symbol length.

Keep in mind that the requirements of the simple CRL format are hardly a stretch for any good assembler. Macro processing and relocatable output are not necessary. In any case, you probably have other good uses for a top-run assembler, so the SLR products are strongly recommended, a bargain at their price, and almost unbelievably fast.



Вот здесь всё что Вам надо, чтобы сегодня же начать штамповать игры для РК86 в огромном количестве и изумительном качестве используя компилятор BDS C 1.60 (а также и V2.0/Z, если Вы имеете в вашем РК процессор Z80), а также там же есть исходник компилятора 1.60 (но для нас он имеет лишь исторический интерес). Скачав отсюда, Вы с'экономите затрату часа труда на возню с CP/M-овскими LBR-архивами.

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

Изучая примеры Си-программ для BDS C из американской BDS UG (User Group), это собрание программ от любителей выкладываемое на телефонной BBS посвящённой BDS C, узнал, что оказывается и среди тупых американцев нашлись в начале 80-тых умные люди (что странно, если верить Михаилу Задорнову, открывшему, что "они реально тупые", похоже что 40 лет назад американцы ещё не отупели и были вполне умны, и лишь потом потребление ГМО-продуктов питания их резко отупила).

Оказывается Вильям С.Колли, один из американских программистов, в 1980 году оказался достаточно сообразительным, чтобы как и наши советско/российские люди в начале 90-тых, успешно догадаться, как можно использовать M80/L80 с мнемониками Z80 за счёт имитации в исходнике структуры CRL-файла и написал вот такое руководство.

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

Но те из использующих BDS C, у кого мозг уже с сильно пониженным IQ (вызванным обильным потребление ГМО-продуктов во время детства и юности), тот может перевести Гуглом это руководство. Хотя, как я уже упомянул в этом же посте чуть выше, в таком оформлении исходников теперь уже нет нужды, ибо в дистрибуве BDS C 1.60 имеется CASM нормально воспринимающий Z80-мнемоники.

Спойлер:

 *****************************************************************
 *                                                               *
 *     A Procedure for Generating .CRL Files at the Assembly     *
 *           Source Level Using Microsoft's MACRO-80             *
 *                                                               *
 *                        May, 1980                              *
 *                                                               *
 *                   William C. Colley, III                      *
 *                  305 Memorial Dr. -- #518B                    *
 *                     Cambridge, MA 02139                       *
 *                                                               *
 *****************************************************************

    Because  MACRO-80  has the pseudo-ops .PHASE and .DEPHASE, it can create .CRL files directly  from  assembly  source without any tedious hand patching. The procedure will be described below, and will refer to the example file enclosed which is part of a tape-disk transfer
routine for talking to a COSMAC elf and contains 2 functions.

    The steps in generating the assembly source are easily followed as the code itself is written.  They are:

        1)  Set up MACRO-80 for the job.

        2)  Build the directory for the functions in the .CRL file.

        3)  Write functions which consist of:

                a)  A list of needed functions,

                b)  the length of the function,

                c)  dummy jump instructions for the BDS C linker,

                d)  the code itself with relocation targets marked,

                e)  and a relocation table for the function.

        4)  Mark the end of the package for the BDS C linker.


Setting up MACRO-80

    The setup for MACRO-80 is simple. Declare the whole package to be an absolute segment (ASEG) and set the origin to 0. This makes the package load at location 0 in memory.

Building the Directory

    A directory entry is made up of a DB to define the function'sname, and a DW to define its  address. The last character of the function name must have the upper bit set. Function names are
8-CHARACTERS MAXIMUM. The argument of the DW statement is a label set up at the beginning of the list of functions needed by the function named in the DB. For example:

        DB      'FUNCTIO','N' OR 80H
        DW      FUN1
        .
        .
        .
        DB      80H             ;This terminates the directory.
        DW      NXTADR

FUN1:    <list of needed functions for FUN1>

    The directory has one entry for each function, 31 functions maximum. The directory is  terminated by a null entry with the label on the END statement of the package as its DW argument.

    Following the directory should be an ORG statement to make the code start at location 512+5.  Note that this reserves the full 512 bytes for the directory, and takes care of the 5 reserved bytes at the beginning of the code portion of the .CRL file.

Writing the Function

    The function begins with a list of the names of the C functions called by the function being  written. Note that the first item in this list will bear the label referred to the  the DW statement in the directory. For example:

FUN1:    DB      'GOFORI','T' OR 80H     ;Note that the upper order
        DB      'GNUR','D' OR 80H       ;bit is set on names
        .                               ;here, too.
        .
        .
        DB      0                       ;A null terminates
                                        ;this list.

    Next, the length of the function must be inserted. The assembler can calculate this with a couple of cleverly placed  labels, so why count it yourself? This also causes the assembler to adjust the length when you modify and reassemble  the  program  during debugging. The beginning of body label is placed on the first line of code in the routine, and the end of body label is placed JUST AFTER  the last line of code. For example:

        DW      ENDFN1-BEGFN1
        .
        .
        .
BEGFN1:  <code goes here>
        RET
ENDFN1:

    Now comes the sneaky part. In the definition of .CRL files, all of the code must execute from location 0 despite the fact that it may be loaded anywhere in memory. In particular, the directory just burned locations 0-0204H. MACRO-80 allows assembly of code loaded at some address to be done as if it were loaded  at some other address through the .PHASE directive. Thus, the code after the length of the function (the ... in the sample above) is the single statement:

        .phase  0

    If you have specified any functions in the list of needed functions, you must start the code with a list of JMP instructions. There needs to be one JMP per function in the needed functions list. Thus if our FUN1 calls four functions, the beginning of the code would look like this:

BEGFN1:  JMP     0
        JMP     0
        JMP     0
        JMP     0
        <actual code goes here>
        RET
ENDFN1:

    After the jump table, you just  type  in  your  code.   To aid in compiling  the  relocation  table, you should flag lines of code  that contain  references to addresses  in  the  function  with  distinctive labels.  I use A.1, A.2, A.3,...  for the first function in a package, B.1, B.2,  B.3,...  for  the  second function, and so forth. Look for such  references  in  jump  instructions,   call   instructions, LXI instructions  that  load local table base addresses, LHLD instructions that reference local RAM locations, etc. Note that calls to BDS C functions should call the JMP  instruction in the jump table corresponding to the particular function in the list of needed
functions.

    One remark is in order:  DO NOT KILL BC. Your BDS C program will die a painful  death if you murder the stack frame pointer. If you need the BC pair, push and pop.  HL, A, and the flags may be killed, but I have no experience with DE as I push it as a matter of course. The return value of the subroutine goes back in HL.

    To compile the relocation table for the function, you'll need the length of the relocation table (in words)  and a DW statement for each of the markers placed in the code as per the  paragraph  before  last. For example,

        DW      (ENREL1-$)/2    ;Note that the assembler can
        DW      A.1+1           ;count these, too.
        DW      A.2+1
        DW      A.3+1           ;THE +1 IS IMPORTANT!!!!!
        .
        .
        .
ENREL1:  DW      A.25+1          ;A pretty big function.

    A null relocation table looks like this:

ENREL1:  DW      (ENREL1-$)/2

    After  the  end of function label (ENDFN1 in the above examples), you need to get the  assembly  program counter back on course, so tack on the line:

        .dephase

    Additional functions are in the same form as the first.

Marking the End of the Package

    To mark the end of the package for the BDS C linker, put the label referred to by the null directory entry at the end of the program as follows:

NXTADR:
        END

Assembling the beast

    Now that you have the source  file,  you hack on it with MACRO-80 and LINK-80 as follows:

A>M80 =FILENAME

No fatal error(s)

A>L80 /D:0,/P:0,FILENAME,FILENAME.CRL/N/E

Origin below loader memory, move anyway (y/n)?N
A>ERA FILENAME.REL
A>

    Here, I have assumed that MACRO-80, LINK-80, and FILENAME.MAC live on the currently logged-in disk drive. It's not required, but it makes life simpler.

    You now have  FILENAME.CRL which you feed to CLINK just like any BDS C program. Go to it.

PS. Ремарка для изучающих Си. Гораздо полезнее для изучения Си вместо тупого многократного прочтения Кернигана и Ритчи - изучение чужих исходников на Си. В разных версиях дистрибутивов и разных Си полно примеров, также есть примеры на архивных сайтах по CP/M. Примеров для Паскаля сотни, а для Си - десятки. Это потому, что в начале 1980-тых Си ещё не победил Паскаль, который был до конца 1980-тых намного популярнее.

Общеизвестно, что Си победил Паскаль не в честной конкуренции, а за счёт коварства. Это случилось потому, что на майн-фреймы в американские университеты с начала 80-тых стали бесплатно ставить ОС Unix. А т.к в Unix нет Паскаля, а всё основано как раз на Си (в т.ч. и программный интерфейс), то студентам преподавали именно Си (затем С++), а Паскалю вообще не учили. И естественно, когда на руководящие посты в промышленность пришли студенты выученные таким образом, то они потребовали компиляторы Си и повели мир этой дорогой.

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

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

barsik пишет:Отличная новость для нечокнутых ретро программистов (нечокнутые это те, кто используют в ассемблерных исходниках нормальные мнемоники вместо допотопных).
Новость оказалась далеко не такой хорошей, как исходно казалось!

Выяснилось, что писатель сатирик и юморист Михаил Задорнов был прав, что "америкосы реально тупые" и я обнаружил, что это у них было уже в 1980-тые годы. Во-первых, последние дистрибутивы версий BDS C 1.60 и 2.0/Z выложенные в 2002 году Леором Золманом использовать в том виде, что он выложил не получится. И даже, то что он несколько раз дополнял этот архив не особо помогло.

Простейший Hello World конечно странслируеть можно, но полноценно пользоваться и странслировать примеры на BDS C и из предыдущих BDS-дистрибутивов и из User's Group (BDS C UG) - не получится. Чтобы пользоваться версией 1.60 надо взять дистрибутив 1.50A или 1.51 и в них заменить файлы, что есть в выкладке 2002 года. Или по крайней мере надо иметь более ранние дистрибутивы под рукой.

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

Много примеров из 1980-1981 годов. Тогда в ходу была версия 1.44 и более ранние (1.44 это самая ранняя версия, что можно сейчас найти). При попытке странслировать программу из 1980 года на BDS C 1.60 из 1986 или даже на версии 1.51 из 1985, не говоря уже о версии 2.0 из 1989 года, - линковщик выдаст кучу неразрешённых ссылок и попытки найти эти функции в библиотеках дистрибутива ничего не дадут. Но всё получится, если взять древний дистрибутив из 1980 года и транслировать на нём.

Та же неразбериха с мануалами. В основном они из 1988 года, т.е уже, как минимум к версии 1.60. Потому лучше всего взять настоящий полный дистрибутив 1.50А (это март 1983) с его же документацией и не дёргаться по поводу версии 1.60, если нет полного дистрибутива 1.60. Т.е для начала версии 1.50А достаточно и удобнее, т.к все примеры сделаны не для версии  1.60. Отличия есть, т.к тогда как раз стандарт Си изменился. А для изучения 1.50 лучше, т.к ей как раз соответствует первое издание Кернина и Ритчи из 1984 года (издательство Мир).

Несомненно компиляторы СС и CC2 в версии 1.60 явно лучше (это написано, да и за 3 годы явно были устранены какие-то глюки и что-то за это время было доработано). Но V 1.60 можно попробовать позже, после полного освоения BDS. Потому я решил пользоваться своими древними дистрибутивами BDS C 1.50 из СССР (следует уважать опасный труд советских шпионов).

Оказалось, что ZCASM дохлый. В TSR-эмуляторе странсированная версия не работает, т.к эта версия работат в приводе A:. А в эмуляторе это минимум C:. Потому нужна модификация исходника и перетрансляция. А это по указанным причинам не так то просто. После долгого траха удалось странслировать, сделав рабочим диск на винчестре. Но это не помогло. Уже странслированная для дистрибутива версия, которая работает только если привод A: тоже оказалась не рабочая. Обе версии дохлые. Делают вид, что работают, а на самом дело дают дохлоту, что потом не транслируется и не линкуется. Так почему-то остаются команды КР580 JMP, что Z80 ассемблеры, естественно не понимают. Но если бы только это. Несложно было бы вручную заменять JMP на JP. Но увы, кроме этого в тексте оказывается мусор, с чем разбираться уже нет желания. В общем ZCASM - дохлота, надо разбираться в исходнике.

Тогда взялся за CAZM. Это тоже оказалась ещё та морока. Там вообще COM-файла не было и пришлось транслировать. И естественно в лоб ничего не странслировалось (что обычно, когда выкладывают исходники без болного дистрибутива). После некоторой возни, наконец конвертировалось и даже верно (т.е в соответствии с требованием CRL-формата). Но оказалось, что как я и писал это CAZM сделан под левый ассемблер. А именно под ассемблер SLRMAC. Метки там длиной до 14 символов. Потому подавляюшее большинство ассемблеров, где допустимы метки длиной 6, 6 или 8 символов не годятся. И это даже не считая, что для ASM от Digital Reserch ставится в EQU двоеточие, а в DS и DB двоеточие наоборот не ставится  (такого идиотизма нет в большинстве ассемблеров), отчего для левых ассемблеров пришлось редактировать BDS.LIB и библиотеки.

У меня в архиве около 30 разных ассемблеров, но лишь немного более десятка для процессора Z80 и ни один не годится для CAZM. Мало того, что для большинства Z80 ассемблеров левые псевдо операторы (типа DC и др. или даже совсем идиотских типа .byte). Из кучи ассемблеров (в т.ч. и ZMAC Howly) только Z80ASM странслировал, но всегда выдаёт две каких-то непонятных ошибки. Но HEX-файл даёт. Но это не помогает. В итоге программа насмерть дохлая.

SLR-ассемблеров полно, но нужного SLRMAC нет. И уже его нигде нет, он погиб 30 лет назад, т.к никому был и не нужен (ибо есть M80 и все остальные ассемблеры по сравненю с ним туфта). И всё это потому, что пытаются использовать убогие самодельные ассемблеры не имеющие PHASE. Отчего приходится выкручивать по страшной силе. Тогда как использую M80 имеющего PHASE получается совсем просто, даже вручную делается. А без M80 приходится добавлять кучу меток с длиной имен аж в 14 символов.

Потратив 15 часов на элетротрах с этими мудацкими любительскими американскими конверторами ZCASM и CAZM, плюнул на это. И решил проблему в лоб и тупо с помощью конвертора мнемоник Z80 в мнемоники КР580. Как раз пригодился вполне рабочий и приличный конвертор по имели XZI.COM. Этот конвертор транслирует без проблем. А CASM от Леора Золмана также безупречный. За 15 часов я бы сам написал свой CAZM для М80, ибо M80 позволяет сделать это просто, - без диких извратов, что требуют убогие самодельные ассемблеры.

Теперь увы, - при трансляции убогим ассемблером нельзя вставлять фрагменты с оператором .PHASE. Т.о нельзя скомпилировать эмулятор CP/M-BDOS в обычном ассемблерном фрагменте и при старте просто кидать его под вершину RAMTOP (процедурой инициализации, что делается первым делом при старте СИ-программы). Чтобы это сделать придётся написать программу DATA-GEN (я такую уже писал, но не для Си, а для бейсика), которая берёт на входе файл с кодами странслированными на нужный адрес и формируек блок данных (типа как в операторах DATA в бейсике). Тогда процедура инициализации сможет кидать эмулятор CP/M под вершину ОЗУ (т.е ниже адреса 7600).

Очень возможно, что выгоднее было бы взяться за использование AZTEC Си, а не за использование BDS C. Кроме того Whitesmith C, хоть и требует версию CP/M с высоким TPA (и на машине в 32К не сработает), но по признанию самого Леора Золмана даёт более скоростной выходной код. Этот компилятор у меня есть, но я не разбирался ещё с ним.
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty .

Сообщение  barsik Пт Дек 11 2020, 08:01

4
Отличная новость для нечокнутых ретро программистов (нечокнутые это те, кто используют в ассемблерных исходниках нормальные мнемоники вместо допотопных). Сканируя Интернет в поисках любой полезной информации про компиляторы Си для CP/M скачал выложенный самим Леором Золманом последний пакет обновлений из 2002 года (BDS C Compiler/Linker Retail Distributions and Source Code).

Там оказались не только исходники всех программ пакета компилятора версии 1.60 (что есть последняя версия, что делал сам Леор Золман, т.к версию 2.0 делал уже не он), но и дистрибутив BDS C 1.60 для процессора 8080 из 1986 года. А ранее я использовал и использую сейчас (а точнее на этой неделе) более древнюю версию 1.50A из 1982 года, что единственная имела хождение в СССР, т.к советские разведчики резиденты (т.е шпионы занимающиеся промышленным шпионажем) достали лишь её.

Видимо к 1986 году у наших шпионов задачи воровать инструментальное ПО для ОС CP/M уже не стояло, актуальнее стала задача воровать ПО для MSDOS. Из-за чего всё CP/M ПО, что имелось в СССР, имело дату выпуска не старше 1982 года (вот из-за таких фатальных ошибок ГРУ мы не смогли решить задачу "догнать и перегнать США", чётко поставленную перед советским народом товарищем Н.С.Хрущёвым).

Так вот в дистрибутиве этой версии BDS C 1.60 оказался включённым пакет сторонних разработчиков фирмы XEROX (из 1984 года) содержащий CASM для мнемоник процессора Z80, который конвертирует исходники в формат пригодный для M80/L80 от Microsoft. Как видим нормальных людей уже в начале 80-тых годов предельно утомил этот "лохматый ужас на крыльях ночи", т.е кошмарные мнемоники допотопного процессора и пользуясь тем, что Leor Zolman любезно включил в дистрибутив (в качестве примера использования BDS C) и исходник CASM для 8080 написанный на Си, сумели переделать его под мнемоники более передового процессора Z80. Что в очередной раз доказывает, что только сумасшедшие, имея возможность выбора, пользуются допотопными мнемониками Intel.

Это шикарная новость. Теперь нормальным людям использующим мнемонику Z80 уже не требуется вручную моделировать структуру CRL-файла с постоянным риском что-то перепутать с адресацией или размерами и получить неразрешимую головную боль на десятки часов электротраха. Достаточно просто написать, как обычно подпрограмму на ассемблере Z80 и обрамить её тэгами FUNCTION... ENDFUNC и на этом все заботы программиста о подстыковке ассемблерных фрагментов в Си-программу заканчиваются.

Более того, в эту же выкладку от Леора Золмана оказался включённым и BDS C версии 2.0 из 1989 года для Z80. Этот пакет готовил уже не Леор Золман, а другой человек - какой-то любитель, которому автор Леор Золман доверил сопровождение и поддержку своего компилятора в конце 1980-тых годов, когда это потеряло всякий коммерческий смысл. В этот пакет включён уже другой (универсальный для Z80/8080) CASM (названный CAZM) написанный другим человеком в конце 1980-тых годов и доработанный в 1991 году.

Все стандартные C-функции написанные на ассемблере в этой Z80-версии компилятора переписаны на ассемблер (и соответственно мнемоники) Z80. Разумеется это (т.к алгоритм и использование регистров не меняются), если и даст ускорение, то лишь всего на 5% (благодаря более коротким двухбайтовым JR-командам). А вот код самого компилятора скорее всего оставлен исходным (без использования команд Z80) и эффективность самого выходного кода вряд-ли улучшилась (ибо исходника-то у этого человека не было, он опубликован лишь в 2002 году, да и разобраться в чужом коде вряд-ли было бы легко).

Пока мне не надо производить код для процессора Z80, а надо лишь использовать его мнемоники программируя для процессора КР580, потому я даже к версии 1.60 пока не перейду. Ибо кто её знает... а вот версией 1.50 все кто её использовали в СССР и в России были вполне довольны и даже визжали от восторга.

Лишь заимствую ZCASM из дистрибутива 1.60 (т.е даже не более свежий CAZM из дистрибутива BDS C 2.0 из 1989). Этого ZCASM из 1984 года будет вполне достаточно. Ведь CASM не меняет мнемоники, а лишь вставляет в текст доп.строки дающие нужную структуру CRL-файла, потому без разницы насколько он крут.

Любопытно, что в READ.ME файле Леор Золман признаёт, что его BDS C уступает в скорости кода Whitesmith C из-за того, что он принял bad decision использовать пару BC для хранения адреса стека с параметрами на нём, что, или лишало возможности использовать BC в функциях и процедурах, или требовало ставить лишние PUSH-POP, что также снижало скорость.

Вот начало ДОК-файла про ZCASM:

Спойлер:
       Copyright (c) 1984 Brian Waldron, Xerox Corp. based on CASM by Leor Zolman

This document describes the ZCASM Assembly language --> CRL-Format Preprocessor for BDS C 1.5. The original program, CASM.C, has been modified to process ZILOG mnemonics and output short labels for assembly with Microsoft's M80/L80.

The files making up the ZCASM package are as follows:

ZCASM.DOC - this file
ZCASM.C - source file for ZCASM program
ZCASM.SUB - submit file for performing conversion of CZM file to CRL

Which looks like this:

       ZCASM $1
       M80 =$1/Z
       L80 $1,$1.CRL/N/E


А вот начало документации к более позднему по времени конвертору исходников в CRL-формат по имени CAZM. Этот чувак похоже написал какой-то свой левый CLRMAC Z80 макро-ассемблер и потому ломит под него: "SLR products are strongly recommended... and almost unbelievably fast". Как будто стандартного микрософтовского M80/L80 ему мало. А скорее всего он его просто не купил (ибо он стоил $300, что не каждому по карману), потому и использовать не мог.

В пропаганду своего ассемблера автор CAZM несёт полную пургу: "users might like a more generalized program for fast, flexible, modern assemblers such as SLR Systems' SLRMAC and Z80ASM." SLRMAC я не имел и не знаком с ним, но Z80ASM знают все и это полнейший примитив, слабее которого нет ничего. Его даже сравнивать с M80 нелепо и даже ставить рядом нельзя. А насколько его собственный SLRMAC ассемблер убогий видно уже по необходимости для конверсии в COM-файл дополнительно использовать ещё и CLOAD), хотя, видимо, его преимущество в скорости трансляции и возможности использовать имена и метки длиной до 14 символов (вместо 6 для Microsoft M80).

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

Спойлер:
       Preliminary Documentation for CAZM v0.3 (beta release)
       a Z80 Assembly_Language_to_CRL_Format Preprocessor for BDS C vZ2.0
       Jim Chapin, March, 1992


          Introduction

The CASM preprocessor is provided with the BDS C Compiler package to convert specially prepared "CSM-format" source files for assembly by DRI's ASM or MAC. Another BDS C program, CLOAD, is used to convert the resulting HEX file to the CRL format that is recognized by the BDS C linkers, CLINK and L2. The process is quick, and requires very little modification of working ASM format
routines.

Unfortunately for Z80 users, CASM was designed for the Intel 8080 mnemonics and limited pseudo-ops supported by ASM and MAC. This reduces the appeal of the assembly-to-CRL translation system for programmers accustomed to an advanced Z80 assembler.

ZCASM is a modification of the basic CASM program that provides preprocessing for the once-popular M80 assembler in Z80 mode.  ZCASM has some limitations of its own, mostly because of the assembler it was designed for. The latest version I know of, v1.5, was written for BDS C v1.50a.

After comparing CASM v1.6 and ZCASM v1.5, it seemed to me that BDS C vZ2.0 users might like a more generalized program for fast, flexible, modern assemblers such as SLR Systems' SLRMAC and Z80ASM. The result is CAZM.

CAZM can be quickly configured for either Zilog or extended Intel (Z80.LIB) mnemonics.  It supports the full Z80 instruction set in either configuration. When CAZM is compiled for Intel mnemonics, the distribution CSM files can be used without alteration.  The intent was NOT to make existing, reliable code obsolete.

CAZM is used the same way as CASM. BDS.LIB can be used without serious alteration (see point below concerning data definition pseudo-ops). A typical conversion might go like this for file FOOBAR.CSM:

       cazm foobar             ;; BDS C/assembly code --> normal assembly source
       slrmac foobar/h         ;; assembly source --> HEX file
       cload foobar            ;; conversion HEX file --> to CRL file

If you do not have one of the SLR assemblers, CAZM may still work as written if your assembler understands the DEFC or DC pseudo-op (see point below), the ELSE pseudo-op, and can distinguish between long labels (14 characters or so). If your assembler produces only REL-file output, you may have problems with link restrictions on symbol length.

Keep in mind that the requirements of the simple CRL format are hardly a stretch for any good assembler. Macro processing and relocatable output are not necessary. In any case, you probably have other good uses for a top-run assembler, so the SLR products are strongly recommended, a bargain at their price, and almost unbelievably fast.



Вот здесь всё что Вам надо, чтобы сегодня же начать штамповать игры для РК86 в огромном количестве и изумительном качестве используя компилятор BDS C 1.60 (а также и V2.0/Z, если Вы имеете в вашем РК процессор Z80), а также там же есть исходник компилятора 1.60 (но для нас он имеет лишь исторический интерес). Скачав отсюда, Вы с'экономите затрату часа труда на возню с CP/M-овскими LBR-архивами.

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

Изучая примеры Си-программ для BDS C из американской BDS UG (User Group), это собрание программ от любителей выкладываемое на телефонной BBS посвящённой BDS C, узнал, что оказывается и среди тупых американцев нашлись в начале 80-тых умные люди (что странно, если верить Михаилу Задорнову, открывшему, что "они реально тупые", похоже что 40 лет назад американцы ещё не отупели и были вполне умны, и лишь потом потребление ГМО-продуктов питания их резко отупила).

Оказывается Вильям С.Колли, один из американских программистов, в 1980 году оказался достаточно сообразительным, чтобы как и наши советско/российские люди в начале 90-тых, успешно догадаться, как можно использовать M80/L80 с мнемониками Z80 за счёт имитации в исходнике структуры CRL-файла и написал вот такое руководство.

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

Но те из использующих BDS C, у кого мозг уже с сильно пониженным IQ (вызванным обильным потребление ГМО-продуктов во время детства и юности), тот может перевести Гуглом это руководство. Хотя, как я уже упомянул в этом же посте чуть выше, в таком оформлении исходников теперь уже нет нужды, ибо в дистрибуве BDS C 1.60 имеется CASM нормально воспринимающий Z80-мнемоники.

Спойлер:

 *****************************************************************
 *                                                               *
 *     A Procedure for Generating .CRL Files at the Assembly     *
 *           Source Level Using Microsoft's MACRO-80             *
 *                                                               *
 *                        May, 1980                              *
 *                                                               *
 *                   William C. Colley, III                      *
 *                  305 Memorial Dr. -- #518B                    *
 *                     Cambridge, MA 02139                       *
 *                                                               *
 *****************************************************************

    Because  MACRO-80  has the pseudo-ops .PHASE and .DEPHASE, it can create .CRL files directly  from  assembly  source without any tedious hand patching. The procedure will be described below, and will refer to the example file enclosed which is part of a tape-disk transfer
routine for talking to a COSMAC elf and contains 2 functions.

    The steps in generating the assembly source are easily followed as the code itself is written.  They are:

        1)  Set up MACRO-80 for the job.

        2)  Build the directory for the functions in the .CRL file.

        3)  Write functions which consist of:

                a)  A list of needed functions,

                b)  the length of the function,

                c)  dummy jump instructions for the BDS C linker,

                d)  the code itself with relocation targets marked,

                e)  and a relocation table for the function.

        4)  Mark the end of the package for the BDS C linker.


Setting up MACRO-80

    The setup for MACRO-80 is simple. Declare the whole package to be an absolute segment (ASEG) and set the origin to 0. This makes the package load at location 0 in memory.

Building the Directory

    A directory entry is made up of a DB to define the function'sname, and a DW to define its  address. The last character of the function name must have the upper bit set. Function names are
8-CHARACTERS MAXIMUM. The argument of the DW statement is a label set up at the beginning of the list of functions needed by the function named in the DB. For example:

        DB      'FUNCTIO','N' OR 80H
        DW      FUN1
        .
        .
        .
        DB      80H             ;This terminates the directory.
        DW      NXTADR

FUN1:    <list of needed functions for FUN1>

    The directory has one entry for each function, 31 functions maximum. The directory is  terminated by a null entry with the label on the END statement of the package as its DW argument.

    Following the directory should be an ORG statement to make the code start at location 512+5.  Note that this reserves the full 512 bytes for the directory, and takes care of the 5 reserved bytes at the beginning of the code portion of the .CRL file.

Writing the Function

    The function begins with a list of the names of the C functions called by the function being  written. Note that the first item in this list will bear the label referred to the  the DW statement in the directory. For example:

FUN1:    DB      'GOFORI','T' OR 80H     ;Note that the upper order
        DB      'GNUR','D' OR 80H       ;bit is set on names
        .                               ;here, too.
        .
        .
        DB      0                       ;A null terminates
                                        ;this list.

    Next, the length of the function must be inserted. The assembler can calculate this with a couple of cleverly placed  labels, so why count it yourself? This also causes the assembler to adjust the length when you modify and reassemble  the  program  during debugging. The beginning of body label is placed on the first line of code in the routine, and the end of body label is placed JUST AFTER  the last line of code. For example:

        DW      ENDFN1-BEGFN1
        .
        .
        .
BEGFN1:  <code goes here>
        RET
ENDFN1:

    Now comes the sneaky part. In the definition of .CRL files, all of the code must execute from location 0 despite the fact that it may be loaded anywhere in memory. In particular, the directory just burned locations 0-0204H. MACRO-80 allows assembly of code loaded at some address to be done as if it were loaded  at some other address through the .PHASE directive. Thus, the code after the length of the function (the ... in the sample above) is the single statement:

        .phase  0

    If you have specified any functions in the list of needed functions, you must start the code with a list of JMP instructions. There needs to be one JMP per function in the needed functions list. Thus if our FUN1 calls four functions, the beginning of the code would look like this:

BEGFN1:  JMP     0
        JMP     0
        JMP     0
        JMP     0
        <actual code goes here>
        RET
ENDFN1:

    After the jump table, you just  type  in  your  code.   To aid in compiling  the  relocation  table, you should flag lines of code  that contain  references to addresses  in  the  function  with  distinctive labels.  I use A.1, A.2, A.3,...  for the first function in a package, B.1, B.2,  B.3,...  for  the  second function, and so forth. Look for such  references  in  jump  instructions,   call   instructions, LXI instructions  that  load local table base addresses, LHLD instructions that reference local RAM locations, etc. Note that calls to BDS C functions should call the JMP  instruction in the jump table corresponding to the particular function in the list of needed
functions.

    One remark is in order:  DO NOT KILL BC. Your BDS C program will die a painful  death if you murder the stack frame pointer. If you need the BC pair, push and pop.  HL, A, and the flags may be killed, but I have no experience with DE as I push it as a matter of course. The return value of the subroutine goes back in HL.

    To compile the relocation table for the function, you'll need the length of the relocation table (in words)  and a DW statement for each of the markers placed in the code as per the  paragraph  before  last. For example,

        DW      (ENREL1-$)/2    ;Note that the assembler can
        DW      A.1+1           ;count these, too.
        DW      A.2+1
        DW      A.3+1           ;THE +1 IS IMPORTANT!!!!!
        .
        .
        .
ENREL1:  DW      A.25+1          ;A pretty big function.

    A null relocation table looks like this:

ENREL1:  DW      (ENREL1-$)/2

    After  the  end of function label (ENDFN1 in the above examples), you need to get the  assembly  program counter back on course, so tack on the line:

        .dephase

    Additional functions are in the same form as the first.

Marking the End of the Package

    To mark the end of the package for the BDS C linker, put the label referred to by the null directory entry at the end of the program as follows:

NXTADR:
        END

Assembling the beast

    Now that you have the source  file,  you hack on it with MACRO-80 and LINK-80 as follows:

A>M80 =FILENAME

No fatal error(s)

A>L80 /D:0,/P:0,FILENAME,FILENAME.CRL/N/E

Origin below loader memory, move anyway (y/n)?N
A>ERA FILENAME.REL
A>

    Here, I have assumed that MACRO-80, LINK-80, and FILENAME.MAC live on the currently logged-in disk drive. It's not required, but it makes life simpler.

    You now have  FILENAME.CRL which you feed to CLINK just like any BDS C program. Go to it.

PS. Ремарка для изучающих Си. Гораздо полезнее для изучения Си вместо тупого многократного прочтения Кернигана и Ритчи - изучение чужих исходников на Си. В разных версиях дистрибутивов и разных Си полно примеров, также есть примеры на архивных сайтах по CP/M. Примеров для Паскаля сотни, а для Си - десятки. Это потому, что в начале 1980-тых Си ещё не победил Паскаль, который был до конца 1980-тых намного популярнее.

Общеизвестно, что Си победил Паскаль не в честной конкуренции, а за счёт коварства. Это случилось потому, что на майн-фреймы в американские университеты с начала 80-тых стали бесплатно ставить ОС Unix. А т.к в Unix нет Паскаля, а всё основано как раз на Си (в т.ч. и программный интерфейс), то студентам преподавали именно Си (затем С++), а Паскалю вообще не учили. И естественно, когда на руководящие посты в промышленность пришли студенты выученные таким образом, то они потребовали компиляторы Си и повели мир этой дорогой.

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

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

barsik пишет:Отличная новость для нечокнутых ретро программистов (нечокнутые это те, кто используют в ассемблерных исходниках нормальные мнемоники вместо допотопных).
Новость оказалась далеко не такой хорошей, как следовало из прочтения сопутствующей документации !!! Точнее оба предлагаемых CASM-конвертора Z80-текстов в CRL-формат пригодный для BDS-линковщика - оказались полным фуфлом и я только впустую потратил на них целый килограмм времени. Соблюдая CRL-формат вручную я бы не потратил столько времени при написании 50-ти программ на Си (ибо ассемблера в программе и не должно быть много, иначе зачем вообще применять ЯВУ?). Что очень обидно. Руки надо отрывать тем, кто гонит такое фуфло.

Выяснилось, что писатель сатирик и юморист Михаил Задорнов был прав, что "америкосы реально тупые" и я обнаружил, что это у них было уже в 1980-тые годы. Во-первых, последние дистрибутивы версий BDS C 1.60 и 2.0/Z выложенные в 2002 году Леором Золманом использовать в том виде, что он выложил не получится. И даже, то что он несколько раз дополнял этот архив не особо помогло.

Простейший Hello World конечно странслировать можно, но полноценно пользоваться и странслировать примеры на BDS C и из предыдущих BDS-дистрибутивов и из User's Group (BDS C UG) - не получится. Чтобы пользоваться версией 1.60 надо взять дистрибутив 1.50A или 1.51 и в них заменить файлы, что есть в выкладке 2002 года. Или по крайней мере надо иметь более ранние дистрибутивы под рукой, чтобы дополнять при трансляции старых примеров.

Ибо при смене версии менялись названия стандартных заголовочных файлов, библиотек и их содержимое, названия функций и процедур изменялись под стандарт Си и дополнялись. И это делал не только автор, но и десятки или даже сотни программирующих на BDS C любителей выкладывающих свои программы и (включаемые в виде примеров в дистрибутив и) расширяющие возможности BDS C библиотек. При этом у каждого из авторов был свой уникальный набор файлов той конкретной версии или из их смеси.

Много С-примеров из 1980-1981 годов. Тогда в ходу была версия 1.42 и более ранние (1.44 это самая ранняя версия, что можно сейчас найти). При попытке странслировать программу из 1980 года на BDS C 1.60 из 1986 или даже на версии 1.51 из 1985, не говоря уже о версии 2.0 из 1989 года, - линковщик выдаст кучу неразрешённых ссылок и попытки найти эти функции в библиотеках дистрибутива ничего не дадут. Но всё получится, если взять древний дистрибутив 1980 года и транслировать на нём.

Та же неразбериха с мануалами. В основном они из 1988 года, т.е они уже, как минимум, к версии 1.60. Потому лучше всего взять настоящий полный дистрибутив 1.50А (это март 1983) с его же документацией и не дёргаться по поводу версии 1.60, если нет полного дистрибутива 1.60. Т.е для начала версии 1.50А достаточно и она для освоения удобнее, т.к все примеры сделаны не для версии  1.60. Отличия есть, т.к тогда как раз стандарт Си изменился. Для изучения версия 1.50 как раз лучше, т.к ей точно соответствует первое русскоязычное издание Кернигана и Ритчи из 1984 года (издательство Мир).

Несомненно компиляторы СС и CC2 в версии 1.60 лучше (это написано в описании, да и за 3 прошедшие года явно были устранены какие-то глюки и что-то за это время было доработано). Но V 1.60 можно попробовать позже, после полного освоения BDS. Потому я решил пользоваться своими древними дистрибутивами BDS C 1.50 из СССР (следует уважать опасный и героический труд советских шпионов).

Оказалось, что ZCASM дохлый. В TSR-эмуляторе странсированная версия не работает, т.к эта версия работает в приводе A:. А в TSR-эмуляторе CP/M это минимум диск C: (т.к это винчестер в Windows). Потому нужна модификация исходника и перетрансляция. А это по указанным причинам не так-то просто.

После долгого электротраха удалось странслировать, сделав рабочим диск на винчестре. Но это не помогло. А уже странслированная для дистрибутива версия, которая работает только если привод A: тоже оказалась не рабочая (её можно запускать только в эмуляторе, где CP/M с эмулируемым приводом A:). Обе версии дохлые. Они делают вид, что работают, а на самом деле дают дохлоту, которая потом не транслируется и не линкуется.

Так почему-то в выходном CZM файле остаются команды КР580 - JMP, что Z80 ассемблеры, естественно не понимают. Но если бы только это. Несложно было бы вручную заменять буквы JMP на JP. Но увы, - кроме этого в тексте оказывается мусор, с чем разбираться уже нет желания. В общем ZCASM - дохлота, надо разбираться в исходнике. Вряд-ли это не работает из-за Z80. ЯВУ не применяют трюков.

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

А именно, как и написано в доке - под ассемблер SLRMAC. Метки там длиной до 14 символов и левые операторы. Потому подавляюшее большинство ассемблеров, где допустимы метки длиной 6, 7 или даже 8 символов не годятся. И это даже не считая, что для ASM от Digital Reserch ставится в EQU двоеточие, а в метках для операторов DS и DB двоеточие наоборот не ставится (такого идиотизма нет в большинстве ассемблеров), отчего для левых ассемблеров пришлось редактировать BDS.LIB и библиотеки.

У меня в архиве около 30 разных ассемблеров и это аж 41 Мб RAR-файла !!! Но из них лишь чуть более десятка для процессора Z80 и ни один не годится для трансляции текста после CAZM-а. Мало того, что для большинства Z80 ассемблеров левые псевдо операторы (типа DC и др. или даже совсем идиотских типа .byte). Из кучи ассемблеров (в т.ч. и ZMAC Howly) только Z80ASM странслировал, но всегда выдаёт две каких-то непонятных ошибки. Но HEX-файл даёт. Но это не помогает. В итоге программа насмерть дохлая.

SLR-ассемблеров полно, но нужного SLRMAC нет. И уже его нигде нет, он погиб 30 лет назад, т.к никому был и не нужен (ибо есть M80 и все остальные ассемблеры по сравненю с ним туфта). И всё это потому, что пытаются использовать убогие самодельные ассемблеры не имеющие PHASE. Отчего приходится выкручивать по страшной силе. Тогда как используя M80 имеющего PHASE получается совсем просто, даже вручную делается. А без M80 приходится добавлять кучу меток с длиной имен аж в 14 символов.

Потратив 15 часов на элетротрах с этими мудацкими любительскими американскими конверторами ZCASM и CAZM, плюнул на это. И решил проблему в лоб и тупо с помощью конвертора мнемоник Z80 в мнемоники КР580 и обычного CASM-а для КР580 от Леора Золмана, ибо он безглючный. Как раз пригодился вполне рабочий и приличный конвертор по имели XZI.COM, которым неделю назад я конвертировал исходник ПЗУ РК86. Этот конвертор транслирует без проблем, как и CASM от Леора Золмана безупречный. За 15 часов я бы сам написал свой CAZM для М80, ибо M80 позволяет сделать это просто, - без диких извратов, что требуют убогие самодельные ассемблеры.

Теперь увы, при трансляции убогим ассемблером нельзя будет вставлять фрагменты с оператором .PHASE. Таким образом нельзя скомпилировать эмулятор CP/M-BDOS сразу на рабочий адрес и при старте просто кидать его коды под вершину RAMTOP (процедурой инициализации, что делается первым делом при старте СИ-программы). Не имея в ассемблере .PHASE, чтобы это сделать придётся написать программу DATA-GEN (я такую уже писал, но не для Си, а для бейсика), которая берёт на входе файл с кодами странслированными на нужный адрес и формирует текстовый блок кодов (типа как делают в операторах DATA в бейсике). Тогда процедура инициализации сможет блок кодов эмулятора CP/M заданного ассемблерными оператора DEFB (и странслированного под другие адреса) кидать под вершину ОЗУ (т.е ниже адреса 7600).

Очень возможно, что я сделал неверный выбор (или как говорят враги bad move) и выгоднее было бы взяться за использование для написания КР580-программ AZTEC Си, а не пытаться использовать, якобы, глюкастого BDS C. Кроме того есть Whitesmith C, который в отличие от BDS C, хоть и требует версию CP/M с высоким TPA (и на машине с TPA в 32К не работает), но по признанию самого Леора Золмана, этот компилятор Си для КР580 даёт более скоростной выходной код. Этот компилятор у меня есть, но я ещё не разбирался с ним (и пока не буду, т.к жалко более 20 часов уже потраченных на знакомство с BDS C).


Последний раз редактировалось: barsik (Ср Дек 23 2020, 10:34), всего редактировалось 1 раз(а)
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty .

Сообщение  barsik Пн Дек 21 2020, 20:00

5
А теперь информация важная для того, кого интересует практическое использование компилятора BDS C для разработки программ для микропроцессора КР580 (слышал мнение, что этот код будет работать и на процессоре Z80, но сам я большой статистики на этот счёт не имею и сталкивался как раз с обратным). Без этой информации вам пришлось бы потратить много времени, чтобы придумать свой способ как можно использовать этот компилятор Си транслирующий под и для CP/M для целей штамповки игр для РК86.

Задача в том, чтобы после трансляции компилятором для CP/M на выходе получать код работающий на базовом РК86 не имеющем ни дисковода, ни CP/M. Т.е на машине не имеющей почти ничего, - у РК86 есть лишь один несчастный RAM-монитор в ПЗУ F800, дающий возможность использовать для хранения программ и данных накопитель на МГ-ленте (со скоростью загрузки в 150 байт в секунду, что соответствует 1200 бод последовательного интерфейса).

Чтобы CP/M программа смогла работать на РК86, т.е чтобы можно было использовать код странслированный компилятором CP/M, достаточно перед стартом, причём обязательно, недисковой CP/M-программы воссоздавать на компьютере среду CP/M, т.е среду с программным интерфейсом CP/M. В программном интерфейсе CP/M используются вызовы CALL 5 и CALL BIOS+n*3, а выход (т.е возврат в ОС) из запущенной программы осуществляется по RET или JMP 0.

CALL 5 это, понятно, вызов функций BDOS. Вершину доступного ОЗУ (в терминах CP/M это вершина TPA) задаёт адрес в ячейках 0006/0007. А адрес начала CP/M-BIOS задаёт содержимое ячеек 0001/0002 (там адрес BIOS+3).

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

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

В первых экспериментах, я просто загружал в ОЗУ сделанный в виде отдельной программы эмулятор CP/M, инициализировал его и запускал программу полученную из под компилятора. Это, естественно, неудобно. Лучше, чтобы нужную среду себе создавала сама программа при её запуске. Потому далее я нацарапал на бейсике программку, которая подключала в конец странслированной Си программы блочок эмулятора снабжённый стартёром, а в адресах 103...105 программы (т.е с оффсетом в три байта от начала) эта программка вставляла CALL на начало этого блока инициализации. Это прекрасно работало, но утомительно и для каждой программы надо было вручную смотреть её размер, соответственно корректировать загрузчик, транслировать его и ещё с клавиатуры задавать его программе конвертора.

В этом случае программа работала так. Первой командой в программе BDS C стоит CPU команда LD SP,7400 (загрузка стека), а следующей командой в удачно оставленные Леором Золманом три байта подставлялась команда CALL INIT, по которой вызывался мой блочок инициализации эмулятора CP/M, который как выше указано, тупо подставлялся в конец Си-программы. Загрузчиком этот блочок из конца программы копировался на раб.адрес 7400...75FF и нужные байты служебных джампов CP/M записывались в ячейки 0000 и 0005. Этот блочок требовалось транслировать на конкретный адрес.

Из CALL на инициализацию запущенной программе управление возвращалось по RET, после чего стартанувшая программа (считав байты с адресов 0001 и 0006) считала, что она запущена в CP/M с TPA в 29 кб и адресом CP/M-BIOS на адресе 75F0. 29 Кб это, кстати, не мало.

Сравните с тем, что у автора этого компилятора Леора Золмана, когда он писал свой BDS Си было всего 32 Кб на его 2-х мегагерцовом компьютере IMSAI 8080 (правда у него ещё имелся текстовый адаптер с объёмом экранного ОЗУ в 1 кб, обеспечивающий экран в формате 64*16). А с учётом того, что CP/M отжирала от ОЗУ на свои нужды аж 8 Кб оперативки, то что у него у него оставалось для CP/M-программы?

Кстати, Леор Золман в интервью вспоминает, что чтобы выиграть ещё полкило ОЗУ ему пришлось придумать драйвер вывода на экран выводящий лишь на часть экрана. Впоследствии эту идею (ясно, что независимо) советские инженеры разработчики придумали использовать на БК-010, где ОЗУ тоже очень не хватало (там свободного для программ ОЗУ вообще жалкие крохи, т.к из общих 32 Кб половину сжирает крутой графический экран).

Естественно, прицеплять извне к каждой CP/M-программе вручную блочок эмуляции CP/M неудобно. Разумнее заставить это делать компилятор, точнее странслированную им программу. Для этого блочок эмуляции BDOS и BIOS CP/M я оформил в виде Си-процедуры обозванной INIT. При этом в любой Си-программе странслированной для РК86 нужно ставить первой командой вызов процедуры INIT(). Таким образом эта процедура получает управление сразу после инициализации RUN-тайм библиотеки (а физически сразу после метки MAIN) и делает то же самое - создаёт на РК86 бездисководную среду CP/M. Это избавляет от хлопот и экономит время по сравнению с вышепредставленными вариантами.

В таком варианте при линковке достаточно прилинковывать модуль SYS.CRL (обязательно до сканирования стандартных библиотек DEFFx, т.к подменяется стандартная интерфейсная процедура BIOS() ). Этот представленный ниже CRL-модуль (файл по имени SYS.Z80) достаточно обработать CASM-ом, странслировать ASM-ом, затем используя ZSID3 конвертируем объектный HEX-код в CRL-файл. Естественно, т.к ни CASM, ни ASM не понимают нормальные мнемоники сначала надо использовать конвертор мнемоник (я использую XZI.COM - он безупречен, в отличие от других конверторов).

Как видите, происходит как бы обман программы, она вызывает BDOS командой CALL 5, но попадает на 75F6, на адрес JBDOS, где встречает вовсе не BDOS, а ещё один дополнительный промежуточный JMP на настоящий код BDOS (что при версии компилятора 1.50а находится в районе ~$600). А чтобы обслуживался наш CP/M-BIOS мы заменяем своим кодом стандартную процедуру BIOS(), т.е как раз ту, используя которую программа на BDS С взаимодействует с ОС. Как видите теперь мы перехватываем весь интерфейс с операционной системой и в качестве консольных подпрограмм CP/M-BIOS используем стандартные п/п-ммы ПЗУ F800 в РК86.

Это стало возможным лишь благодаря тому, что Леор Золман дал полную документацию к компилятору - дал исходник не только всех библиотек, но и RUN-тайм модуля. А вот с другими 8-ми разрядными ЯВУ (из тех, что я пробовал), т.е бейсиком компилятором, Паскалем МТ+, Турбо-Паскалем и PLMX ничего похожего сделать не удалось из-за отсутствия исходников RUN-тайм модуля.

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

Если на PC стоит ОС Win XP, то удобно использовать TSR эмулятор 22NICE и соответствующий BAT-файл. Естественно параметр ERROR LEVEL CP/M-программы в окружение MSDOS передавать не умеют, потому полностью автоматизировать не выходит. А я пока использую трансляцию в своём эмуляторе ОРИОНА используя в качестве командного файла - AUTOEXEC.SUB (это лучше, чем SUBMIT, т.к пакетные команды выдаются на консоль из тимплета консоли, без записи на диск файлов типа $$$.SUB, как это происходит при работе SUBMIT). Мне это удобнее лишь потому, что сразу же в эмуляторе ОРИОНА я и проверяю странслированную программу. Что возможно потому, что ОРИОН по вызовам ПЗУ совместим и я работаю только с текстом (без графики) и ввод/вывод делаю только стандартно подпрограммами ПЗУ.

Но это ещё не все секреты, что позволят вам начать штамповать в огромном количестве и изумительном качестве игры для РК86. Требуется ещё отредактировать файл CCC.ASM, т.е исходник RUN-тайм библиотеки.

Привожу исходник SYS-модуля, т.к сейчас у меня после полной перестановки Windows XP вообще нет Яндекс.диска, выложить некуда (а остальные облака из тех, что я испытал, уже несколько лет назад не работали в Win XP, не то что сейчас).

Спойлер:

Вообще Яндекс это похоже очень мерзкая фирма, т.к плохо заботится о пользователях. И к тому же ещё очень нагло и повсюду всем старается впиндюрить свой Яндекс-браузер (что у них там шпионящий за людьми код или ещё какая гадость?). Ну ладно бы, можно его загрузить и пользоваться, если это вестчь, - нам от правительства скрывать нечего. Так хрен там, блин, - не работает он на Win XP, хотя указано, что якобы работает. Возможно и можно его заставить заработать, если накачать из Интернета недостающих в Win XP с SP1...SP3 DLL-модулей (но однозначно, если не работает без извратов, то это и есть дерьмо).

И их Яндекс.диск, что был последним шансом для фанатов Win XP иметь 10 Гб в облаке с каждой версией становился всё хуже и хуже совместимым с Win XP, а пару лет назад совсем утратил совместимость. Причём их древнюю версию, что совместима с XP ещё приходится долго искать.

И вообще, блин, из-за этого мерзкого придурка Билла Гейтса, а именно из-за того, что он подло кинул MSDOS (начиная с Win 7), через год придётся выбрасывать Пентиум и ставить вместо него на стол древний комп 486DX2-80, ибо больше не на чем будет прогонять программы MSDOS. Уже пару лет как последние производители браузеров прекратили их делать под Win XP. Последним поддерживаемым браузером был Mozilla Firefox V 52.9. Ещё более-менее пригоден Maxthon V5. На браузерах всего 3-4 летней давности уже половина сайтов не работает, а через год похоже и эти последние сделанные для XP браузеры Firefox и Maxthon перестанут работать. Например поганый Яндекс не желает показывать картинки и давать допуск к облакам уже сейчас.

К тому же оба эти браузера паршивые и неудобные. Самые удобные браузеры были 10 лет назад, а эти браузеры сделаны идиотами для идиотов. Если в простейшей Operе 12 из 2012 года (эта последняя Опера со скоростным движком и ещё в нормальном интерфейсе) открыть десяток страниц и затем посмотреть занимаемое ОЗУ и расход ресурса процессора, то будет занято менее 100 Мб памяти и единицы процентов ресурса CPU. А если то же самое открыть в Firefox 52.9 и подобном современном браузере 2018-2019 года, то ОЗУ компьютера на это истратится ~800 Мб и ~30% ресурса CPU. Это всё индийские программисты, чтобы меньше трудиться, используют всё более и более навороченные компиляторы для всё более и более высокого уровня ЯВУ, что жрут ресурсы машины по страшной силе.

Компиляторы Си для программирования РК86 Firefox.1608574167


Так вот. Берёте исходник CCC.ASM из BDS С версии 1.50а (или 1.51). Ищете метку init или вот такой код, который д.быть где-то на строке номер 768 (а если у вас версия BDS C другая, то всё-равно код вряд-ли сильно изменился, просто ищите нужное место от метки init):

Спойлер:

      IF      CPM             ;under CP/M: clear console, process ARGC & ARGV:
        mvi    c,cstat         ;interrogate console status to see if there
        call   bdos            ;  happens to be a stray character there...

        ora    a               ;(used to be `ani 1'...they tell me this works
        nop                    ; better for certain bizarre CP/M-systems)

        jz     initzz
        mvi    c,conin         ;if input present, clear it
        call   bdos

initzz: lxi     h,tbuff         ;if arguments given, process them.
        lxi    d,comlin        ;get ready to copy command line
        mov    b,m             ;first get length of it from loc. base+80h
        inx    h
        mov    a,b
        ora    a               ;if no arguments, don't parse for argv
        jnz    initl
        lxi    d,1             ;set argc to 1 in such a case.
        jmp    i5

и убираете в нём вызовы CALL BDOS (что транслируются, естественно в CALL 5, так что название метки неверно) и также делаете программный обход неуместного для нас на РК86 разбора командной строки CP/M. Сначала там зачем-то то Леор Золман сделал сброс консольного буфера при входе в программу (ну возможно у него была клавиатура с дребезгом), а далее идёт анализ командной строки в буфере $0080.

Но нам на РК86 это и даром не надо (ибо командная строка, как и ОС у нас отсутствует как класс), и даже наоборот только приводит программу к неприятному улёту (ибо делается CALL 5 в то время, когда на адресе 5 ещё ни хрена не загружено).

Конечно можно было бы делать все РК-программы из под Си работающими с адреса 0 и cделать 248 байт в участке 0008...00FF пустыми (бутафорскими) лишь ради того, чтобы на адресе 0005 сразу стоял необходимый JMP BDOS, отчего CCC.ASM не понадобилось бы даже чуть-чуть корректировать. Но это было бы слишком тупо даже для психов всё ещё желающих программировать для РК86.

Если вам лень разбираться, как откорректировать этот исходник, то зыркните, как тупо и в лоб сделал это я. Это тупо, но зато просто, а главное - работает. Видите - первый CALL BDOS я закомментировал точкой с запятой, а вместо этой закомментированной строки CALL BDOS я вставил свою трёхбайтовую команду JMP OOO. С метки OOO происходит уход в продолжение инициализации в случае, если ком.строки вообще нет, что нам и надо.

В общем, как-то так или, как говорил Аркадий Райкин в кинофильме "Мы с вами где-то встречались": примерно в таком духе, в таком разрезе и редактируйте ваш файл CCC.ASM, а затем транслируйте его CP/M-овским ASM-ом от Digital Research. Ассемблер M80 тут не годится, ибо для ASM.COM как в эр-кашном "Микроне" ставятся двоеточия в метках по EQU, что полный хоккеизм. Ну ладно метки без точек это ещё более-менее по человечески, так во многих ассемблерах, но метки EQU с двоеточием это верх цинизма и насилие над человеческой логикой.

Спойлер:

      IF      CPM             ;under CP/M: clear console, process ARGC & ARGV:
        mvi     c,cstat         ;interrogate console status to see if there
;       call    bdos            ;  happens to be a stray character there...
        JMP     OOO
        ora     a               ;(used to be `ani 1'...they tell me this works
        nop                     ; better for certain bizarre CP/M-systems)

        jz      initzz
        mvi     c,conin         ;if input present, clear it
        call    bdos

initzz: lxi     h,tbuff         ;if arguments given, process them.
        lxi     d,comlin        ;get ready to copy command line
        mov     b,m             ;first get length of it from loc. base+80h
        inx     h
        mov     a,b
        ora     a               ;if no arguments, don't parse for argv
        jnz     initl
OOO:    lxi     d,1             ;set argc to 1 in such a case.
        jmp     i5

Тут красным цветом я показал, что конкретно надо добавить в исходник CCC.ASM. Правда же это совсем немного (а именно всего 12 символов !), это каждый может сделать сам и выкладывать это на файлообменник нет смысла (тем более откуда мне знать какую конкретно версию BDS C вы используете, да и лучше знать, что следует сделать, чем просто брать готовый результат).

Ну и вот сам исходник модуля SYS.CRL (содержащий блок эмуляции консольных ф-ций CP/M), который будучи подлинкованным в Си программу позволяет ей работать на РК86:

Спойлер:


        include   <BDS.LIB>

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

MYBIOS: EQU     07600H-16
JBDOS:  EQU     MYBIOS-100H+6

; ─────────────────────────────────────────────────

        function INIT
        external CPM22, SCOUTA

        PUSH    BC

        LD      A,0C3H
        LD      (0),A
        LD      HL,MYBIOS+3
        LD      (1),HL

        LD      (5),A
        LD      HL,JBDOS
        LD      (6),HL

        LD      (JBDOS),A
        LD      HL,CPM22
        LD      (JBDOS+1),HL

        LD      (0010H),A
        LD      HL,SCOUTA
        LD      (0011H),HL

        LD      (0018H),A
        LD      HL,RST18
        LD      (0019H),HL

        LD      HL,BIOCOD
        LD      DE,MYBIOS
        LD      BC,15

JJJ$03: LD      A,(HL)
        LD      (DE),A
        INC     HL
        INC     DE
        DEC     BC
        LD      A,B
        OR      C
        JP      NZ,JJJ$03
        POP     BC
        RET

RST18:  EX      (SP),HL
LOOP18: LD      A,(HL)
        INC     HL
        OR      A
        JP      Z,EXIT18
        RST     10H
        JP      LOOP18

EXIT18: EX      (SP),HL
        RET

BIOCOD: JP      0F800H          ; +0
        JP      WBOOT           ; +3
        JP      MSTAT           ; +6
        JP      MCONIN          ; +9
        JP      MCOUT           ; +0C

        endfunc

; ─────────────────────────────────────────────────

        function CPM22
        external SPACE, HEXEXI

        LD      A,C
        OR      A
        JP      Z,WBOOT
        CP      12              ; ф.12 возвращает номер версии ОС
        JP      NZ,JJJ$01
        LD      A,22H
RETURN:
        LD      L,A
        LD      B,0
        LD      H,B             ; B=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
        CP      10
        JP      Z,FRDBUF        ; F.10 RD_BUFF
        CP      11
        JP      NZ,BADFUN
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
        POP     HL
        RET

NSTA$2: INC     HL
        LD      A,(HL)
        OR      A
        JP      NZ,NSTA$1
        CALL    XF81B
        INC     A
        JP      Z,POPHL
        DEC     A
        LD      (KBDBUF),A
NSTA$1:
        LD      A,1
POPHL:  POP     HL
        RET

; ─────────────────────────────────────────────────

MSGCPM: EX      DE,HL
MSGLOO: LD      A,(HL)
        CP      '$'
        RET     Z
        RST     10H
        INC     HL
        JP      MSGLOO

; ─────────────────────────────────────────────────

COUNT:  defb    255
KBDBUF: defb    0               ; эти ячейки должны идти подряд

; ─────────────────────────────────────────────────

FUNC$6: LD      A,E
        INC     E
        JP      Z,COD$FF
        INC     E
        JP      Z,NSTAT
        RST     10H
        RET

COD$FF: CALL    NSTAT
        OR      A
        RET     Z
FCONIN:
        LD      HL,COUNT
        LD      (HL),255
        INC     HL
        LD      A,(HL)              ; KBDBUF
        OR      A
        JP      NZ,CONI$2
        CALL    MCONIN
CONI$2: CP      3
        JP      Z,MYBIOS
        RST     10H
        RET

; ─────────────────────────────────────────────────

FRDBUF: LD      L,E
        LD      H,D
        LD      C,(HL)          ; BUFF SIZE
        LD      B,0
        INC     DE
        INC     HL
RDBLOO:
        CALL    FCONIN
        CP      8
        JP      Z,ZABOJ
        RST     10H
        CP      13
        JP      Z,RDDONE
        CP      10
        JP      Z,RDDONE
        CP      3
        JP      Z,MYBIOS        ; CNTRL-C
        INC     HL
        LD      (HL),A
        INC     B
        LD      A,C
        CP      B
        JP      NZ,RDBLOO
RDDONE:
        EX      DE,HL
        LD      (HL),B
        RET

; ─────────────────────────────────────────────────

ZABOJ:  LD      A,B
        OR      A
        JP      Z,RDBLOO
        DEC     B
        DEC     HL
        LD      A,8
        RST     10H
        CALL    SPACE
        LD      A,8
        RST     10H
        JP      RDBLOO

; ─────────────────────────────────────────────────

BADFUN: PUSH    AF
        RST     18H
TBDFUN: defb    1FH,10,'? BDOS ',0
        POP     AF
        JP      HEXEXI

        endfunc

; ─────────────────────────────────────────────────

        function BIOS
        external HEXEXI
       
        CALL    MA1TOH
        EX      DE,HL
        CALL    MA2TOH
        PUSH    BC
        PUSH    HL
        LD      HL,MYBIOS
        LD      A,E
        ADD     A,A
        ADD     A,E
        CP      13
        JP      NC,BADCAL
        LD      E,A             ; put in DE
        LD      D,0
        ADD     HL,DE           ; add to base of jump table
        POP     BC
        PUSH    HL              ; and save for later
        LD      HL,RETADD       ; where call to BIOS will return to
        EX      (SP),HL         ; get address of vector in HL
        JP      (HL)            ; and go to it...

RETADD: LD      L,A             ; all done. now put return value in HL
        LD      H,0
        POP     BC
        RET

BADCAL: PUSH    AF
        RST     18H
TWRONG: defb    1FH,10,'? BIOS + ',0
        POP     AF
        JP      HEXEXI

        endfunc

; ────────────────────────────────────────────────

        function HEXEXI

        CALL    MHEXA
        CALL    MCONIN
        RST     0

        endfunc

; ──────────────────────────────────────────────

        function EXIMON

        RST     18H
        defb    27,89,24+20H,20H,13,10,0

        LD      BC,0
EXIMO1: EX      (SP),HL
        EX      (SP),HL
        DEC     BC
        LD      A,B
        OR      C
        JP      NZ,EXIMO1
        RST     0
               
        endfunc



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

Этот системный модуль для интерфейса с железом РК не полный. Точнее он годится только для базового режима РК86 получаемого по сбросу, т.е для формата экрана 64*25. Однако для игр с использованием РК-шной как бы псевдографики этот режим не выгоден (я пишу как бы потому что во всём море под псевдографикой понимают иное). Здесь речь о блочно-текстовой графике, под которой понимается имитация пиксельной графики текстом с особым шрифтом.

Для игр с использованием особых граф.фонтов с разложением знакоместа 2*2 (такие символы как раз уже есть в фонте РК86), 3*2 и 2*3 важно, чтобы не было межстрочных разрывов. Для этого ВГ75 перепрограммируют на режим в 38 полных строк и высоту знакоместа в 8 линий растра (в базовом режиме по сбросу задаётся 30 строк высотой в 10 линий, из которых 5 строк тратятся на бордюр). Теоретически в этом режиме в экран должно умещаться 32 строки, но из-за неточной центровки РК-игры обычно делают вывод только на 30 строк (остальные 8 строк служат программно формируемым бордюром и на них программе ничего нельзя выводить (кроме символов с кодами $00, $20 или $FF), иначе сорвётся синхронизация.

Однако базовый ROM-BIOS РК86 (т.е то, что прошито в его ПЗУ F800 размером в 2 Кб) поддерживает только режим 64*25, а текстовый режим 64*30 не поддерживатся. Даже бейсиковый оператор PLOT, что как раз предназначен для вывода "псевдографики" работает только в режиме 25-ти строк. Это связано с тем, что 38 строк по 78 байтов занимают гораздо больший объём, чем 30 строк по 78 байтов. В итоге, если экран разместить как и базовый экран сразу ниже вершины физического ОЗУ (т.е сразу под $8000), то разбухшим экраном затирается вся область служебных рабочих ячеек ПЗУ (7600...76CF) и происходит фатальный улёт при попытке вызвать стандартные подпрограммы ПЗУ F800.

А т.к при разработке игр в первую очередь интересен именно режим без межстрочных вертикальных разрывов, то получается, что для разработки таких игр нужен свой новый драйвер текстового вывода. Его несложно написать. И из скоростных соображений лучше это сделать на ассемблере. Удобнее всего, конечно было бы, если бы ПЗУ F800 было бы не 2 Кб, а 4 Кб или даже более (и не важно как, хоть сплошным участком F000...FFFF или в виде 2-х окон по 2 Кб в области F800), тогда можно сохранить стандартные входы для экранных процедур и опроса клавиатуры (опрос клавиатуры тоже меняется при смене адреса экрана, т.к в РК курсор аппаратный).

Не имея такого продвинутого ROM-BIOS каждая РК-игра для псевдографики должна или работать прямо по экранному буферу (что всегда в РК-играх и делалось) или содержать в своём коде свой драйвер ввода/вывода (для экрана и клавиатуры).

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

А для системного модуля SYS.CRL для создания программ с псевдографикой следует дабавить в него ещё несколько процедур. Во-первых, - две процедуры установки режима. Это установка видеорежима 64*30 не имеющего межстрочных разрывов (или возможно 64*43 или 64*51, если использовать высоту знакорядов не 8, а 6 или 4 соответственно) и процедуру возвращающая обычный видео режим 64*25, который в игре может быть нужен для выдачи текстов. А для самой игры осуществляющей вывод в формате 64*30 (или м.б. 64*51) достаточно иметь ещё две процедуры SYMOUT и GRPLOT. Первая представляет собой вывод символа с заданным кодом в указанную координату X,Y, а вторая выводит зажжённый или погашенный пиксель в указанную позицию экрана.

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

Некоторые не особо узколобые из читателей форума посмотревшие глазами на код представленного выше модуля связи между вызовами CP/M и железом РК86 заметили в процедуре INIT() инициализацию трёх RST-входов. Ну про назначение RST 0 в CP/M всякий и так знает, а вот использование RST 10H и RST 18H  не является жёстким стандартом CP/M.

Собственно CP/M предусматривает использование RST на нужды пользователей и даже одно RST обычно занимается системным отладчиком (обычно RST 30H), а RST 38H обычно сжирается прерываниями. В грамотных компьютерах RST используются по максимуму ибо это скоростные вызовы подпрограмм, заметно экономящие память и немного скорость.

В своих реализациях CP/M для Специалиста и ОРИОНА я также использовал все RST. И естественно, уже привык, по крайней мере к использованию самых ходовых RST 10 и RST 18 и впоследствии всегда вставляю их во все даже не CP/M-программы и это хорошо окупается (для игр ещё полезно RST 28 - быстрый вызов п/п чтения на лету F81B).

Нет противопоказаний к тому, чтобы поиметь пользу от быстрых и коротких команд RST и для программ РК86 странслированных на БДС Си. RST 10 - это вывод на F809 содержимого регистра А (без порчи регистров), а RST 18 - это вывод текста до нуль-терминатора располагаемого сразу за кодом RST 18, что экономит 5 байтов на каждом выводе на экран текста. А ещё экономичнее вместо нуль терминатора использовать также выставленный старший байт, тем более, что в РК86 кодировка семибитовая. Но Си работает с ASCIIZ строками.

Таким образом RST 10 и RST 18 это просто RST-входы для двух ходовых подпрограмм SCOUTA (название от Saved Console OUT from Accumulator) и подпрограммы RST18 (обычно в ASM-программах называемая RST_18, но убогий ASM.COM не понимает символ подчёркивания, а предложенный в нём в качестве разделителя символ $ выглядит вообще идиотски).

В РК-играх полезно использовать и команды RST для ускорения опроса клавиш, но тут выгоднее инсталлировать это уже для каждой конкретной программы и использовать прямой доступ к клавишной матрице (т.к это быстрее, чем выполняется п/п-мма F81B).

Спойлер:

Вообще собираюсь использовать на РК86 аппаратную клавиатуру - венгерский Консул. Именно венгерский, а не чешский. Чешский Консул был контактный, менее надёжный. А венгерский вечный - в нём контакты на датчиках с эффектом Холла. Это по работе как герконы (т.е срабатывают от магнитного поля), но никакого движения, всё полупроводниковое. За 35 лет не сдохло (да есть и два запасных датчика на эффекте Холла). Для ИРИШИ эта клавиатура мне не очень подошла (какого-то кода там не хватало, а менять иришин ROM-BIOS, чтобы сделать перекодирование я 30 лет назад ещё не умел). Так и провалялась эта клавиатура без дела 30 лет (да и она не очень эргономичная, т.к высокая). Одно время я её использовал на РК86 с ОЗУ в 60 Кб, но после, когда ради совместимости пришлось вернуть ОЗУ на 32 Кб вернул и матричную клавиатуру.

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

Для игр этих 16 клавиш хватит. Там вместо двух ненужных клавиш РК-шного бокового блока клавиш будет распаян <пробел> и <ВК> (возможно ещё УС или СС). Курсорных, функциональных и клавиш <ВК>, <пробел> хватит для всех игр. Матричные клавиши удобнее аппаратных в том, что позволяют определять двойные нажатия. А аппаратаная клавиатура, типа той, что стоит в ИРИШЕ или ОКЕАНЕ для игр не очень удобная, т.к выдают ASCII-код по нажатию и не дают инфомации о дальнейшем удержании клавиши (т.е не выдают, как PC-шная клавиатура код про отпускание клавиши).
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty .

Сообщение  barsik Пн Дек 28 2020, 22:11

6
.
barsik
barsik
Ветеран

Сообщения : 1149
Дата регистрации : 2016-11-10
Откуда : С-Петербург

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

Компиляторы Си для программирования РК86 Empty Re: Компиляторы Си для программирования РК86

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

7

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


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

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


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