Public Spirit #01
17 мая 1997
  Софт  

Дискуссия - Неплохо бы ассемблер обновить. "Почему так мало больших программ выходит для Спектрума?"


Программное обеспечение Дискуссии и размышления


"Неплохо бы ассемблер обновить"

 музыка:            автор:
(с) Dave      (с) В.Медноногов


   Почему  так   мало   больших
программ выходит для Спектрума?
Имхо, одной из причин  является
трудность             написания
значительных     по     размеру
исходников    на    ассемблере.
Тормозить процесс начинает  уже
не скорость ассемблирования  (и
на Спектруме, и  на  РС  сейчас
есть  высокоскоростные   асмы),
просто  подавляет   сам   объем
информации  -  куча  файлов   с
исходниками  в   сотни   строк,
вечная   путаница   с    вх/вых
параметрами, да  и  сам  текст,
даже      обильно       политый
комментариями, остается  трудно
читаемым.  Последние     месяцы
отладки    заполнены     обычно
шуршанием     распечаток      с
листингами, попыткой
состыковать        разрозненные
процедуры  и  поиском  "глупых"
ошибок, которые с точки  зрения
ассемблера     ошибками      не
являются. Какие на сегодня есть
альтернативы? Кто-то    скажет:
Си,   Паскаль.   В     качестве
единственно доступного  примера
рассмотрим    продукты    фирмы
HiSoft.    Если       отбросить
стандартную         библиотеку,
насильно  цепляемую  к   каждой
программе, анализ генерируемого
кода покажет  как  минимум  два
слабых      места:     передача
параметров   функциям   ведется
относительно          индексных
регистров, а   все   переменные
хранятся в памяти (даже  в  Си,
даже те, для  хранения  которых
хватило бы и набора  регистров)
естественно, это резко понижает
быстродействие  и   значительно
увеличивает  длину   объектного
кода.  В    смысле,  нормальный
программер так писать не будет.
Можно,   конечно,   было     бы
попытаться    написать    новый
компилятор    Си,  но  вряд  ли
удастся с кандачка это  сделать
оптимально. Мне  кажется, более
реальный  путь   -   попытаться
"навернуть"   сам    ассемблер.
Первый шаг на этом пути уже был
сделан: макросы. Вторым   шагом
явилось появление  в  некоторых
ассемблерах  команд, заменяющих
цепочку      команд,    которые
являются  (по  мнению  авторов)
наиболее часто  встречающимися.
Примерно   отсюда   и   следует
плясать  (имхо). Вот  несколько
мыслей:
 1. Все  команды
ассемблера остаются в целости и
неприкосновенности,  и    могут
быть всегда вставлены  в  любое
место    программы.
 2. Чтобы
ассемблер отличал новые команды
от стандартных, надо или давать
им какие-либо новые  имена, или
ставить  перед  командой  спец.
символ: <.>, <$>, <#>  или  др.
 3. Переменные     описываются
стандартным  образом  -   через
DEFB, DEFW - что соответственно
определяет их тип. Впрочем, при
желании  можно  ввести   всякие
INT, BYTE, ARRAY и т.д., и т.п.
 4.Переменные, названия которых
совпадают    со    стандартными
регистрами, должны  и   служить
идентификаторами           этих
регистров. Переменные  в   виде
мнемонических   имен   являются
просто  константами. Заключение
имени  переменной  в   скобочки
является  ссылкой   на    нее.
 5. Тогда  можно ввести понятие
выражения         присваивания.
Hапример:

B=(DAT0)+5C+(DAT1)+DAT2-(HL)

  В  принципе,
чтобы     замедлять      работу
дешифратора     команд,   можно
договориться   ставить    перед
такой строкой LET или  один  из
предлагаемых спец.символов. Эта
строка  преобразуется  в  нечто
типа:

 В   кач-ве   базисного
набора  операций   ограничиться
+,-, простейшими    логическими
(or,xor и т.д. - в  нотации  Си
или Gens) и  операциями  сдвига
(>>,<<). Дополнить по  вкусу :)
Конечно,    надо      оговорить
приоритет      операций      (в
простейшем  случае  -  у   всех
равный), можно ввести скобочки.
Узким местом будет вхождение  в
кач-ве   параметра    выражения
аккумулятора  и   HL, а   также
преобразование  типов. Решается
просто -  побольше  ограничений
на    пользователя .   Hапример
ограничится   выражениями    из
одного-двух    операндов.   Или
запретить вхождение A в  правой
части    иначе    как    первым
параметром. Или   разрешить, но
выдавать warning. Да мало ли...
Побольше  брать   из   Си. Типа
укороченых: A+=5,B>>=2  и  т.д.
Если фантазии совсем не хватит,
сделать  операцию  "равно"  (=)
просто альтернативой LD  -  все
равно      легче      читается.
 6.Массивы.  Hу, оставить   два
стандартных   -   IX   и    IY.
Разрешить  обращение  по   типу
IX[n]. Для   других   (которые,
кстати, определять   теми    же
DEFB,DEFW,DEFS) видимо
прийдется ввести доп  ф-цию. Т.
е. DAT[C] <=> LD  A,C / LD  HL,
DAT / CALL   adr_mas, где   п/п
adr_mas   просто    выдает    в
аккумулятор  значение  элемента
массива, начинающегося  с   HL.
(Черт, опять    скатывание    к
дополнительной     библиотеке.)
Для  скорости  функцию  adr_mas
можно     раскрывать,   хотя...
Впрочем, массивы  можно  и   не
делать.
 7. Подпрограммы.
Т.к. параметры им передаются по
большей  части  через  регистры
общего     назначения      (для
скорости), то  и   принять   во
внимание только такой  механизм
передачи. Hапример:

 PROC sub_name (var HL, var  A,
B) ...
  тело п/п
  ...
  RETURN sub_name

   - если перед  параметром  не
стоит var -  сохранить  его  на
стеке. Тоже самое  относится  к
параметрам после  двоеточия. Т.
е. первая строка примет вид:
 sub_name PUSH BC
         PUSH DE
         ...
   -  оператор  RETURN
предварительно      нормализует
стек, если   необходимо.  Можно
указать имя п/п, к  которой  он
относится   (по   умолчанию,  к
последней   откомпилированой) В
данном случае он превратится в:

         POP DE
         POP BC
         RET

   Eстественно, RETURN'ов может
быть     много.    -      вызов
подпрограммы будет иметь  такой
вид (если  не  нравится  GOSUB,
пусть будет как угодно) : GOSUB
sub_name (20,,C)
   Hапример       так,      что
преобразуется в:
   LD HL,20
  ;второй параметр (A) пропущен
и принимается текущим LD B,C
  CALL sub_name
   Возможно указывать выражения
в   качестве     параметров.
Естественно, при
ассемблировании       проверять
соответствие параметров (а  для
чего  же   всю   эту   петрушку
разводить).
 8.  Чего    еще?  А,
переходы.  Тут     много     не
придумаешь - оставить JR и  JP.
Можно   ввести    универсальный
GOTO,  который    будет     сам
определять, что ставить  -  JR,
JP. Правда, скольки   проходным
станет ваш  ассемблер  это  уже
другой  вопрос .   Можно  взять
вычисляемый  GOTO  из   старого
доброго Фортрана  (его, кстати,
целиком  почти   можно   взять,
отбросив сложную  математику  и
ввод-вывод, всякие  там  WRITE,
READ,FORMAT), который выглядит,
например   так:  GOTO    (ADR1,
ADR2,...ADRn),(switch) ;вместо
(switch) м.быть   выражение   и
преобразуется в:

      LD A,(switch)
      LD HL,_TAB1
      CALL adr_mas2
      JP (HL)

_TAB1 DEFW ADR1,ADR2,...ADRn

   9. Условные   переходы.  Hу,
самое простое - опять  из  того
же Фортрана (чтобы не путать  с
условной   трансляцией, назовем
его  не  IF, а  например, IFS):
IFS  (выражение) METKA1,METKA2,
METKA3  Это   преобразуется   в
вычисление  выражения, за   ним
идет:   OR     A ;нужен,   если
последняя операция не  повлияла
на флаги JP M,METKA1 ;если рез-
т отрицательный  JP  Z,METKA2 ;
если 0 JP  METKA3 ;если  больше
нуля  Можно  добавить   похожий
оператор,    который      будет
реагировать на флаг CY, а не на
SiGN - какой-нить  IFC ... Если
одна из  меток  не  указана, то
соотв. переход отсутствует (это
относится и к случаю, если одна
из меток ссылается  на  строчку
сразу за IF). Как более тяжелый
случай - всевозможные  условные
переходы, типа  IF  (выр1>выр2)
GOTO    metka1    или     более
навороченные  с  использованием
ELSE,  ELSEIF,  ENDIF    Вполне
возможно использовать операторы
тип  begin/end   в   Паскале, а
лучше фигурные скобочки  как  в
Си для  выделения  программнных
блоков (как известно, операторы
обрамления блока  преобразуются
просто во  временные  метки). В
принципе,  это    не    наложит
никаких     ограничений      на
ассемблер. Hе забыть бы  и  про
циклы.  Hаверное,  это    будет
просто красивое обрамление  для
DJNZ и ему подобных.
   10. Метки.
Т.к. они  будут  использоваться
теперь  во   многих   различных
операторах, то  не   плохо   бы
ввести  спецификатор  FAR   для
"дальних"   меток, переход    к
которым  будет   осуществляться
через   JP. Если   спецификатор
отсутствует,   то     ассемблер
вставит  JR   (или   DJNZ   для
цикла). Hапример:
IFC (A>>1) m1, FAR m2, FAR m3

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

   12. Понятно, что
это просто прикидки, конкретная
реализация   -   дело    темное
(особенно   оптимизация). Очень
хотелось  бы   узнать, к   кому
какие мысли приходили  на  этот
счет (если очень большие мысли,
то   можно   мылом). А   может,
литература какая  есть  на  эту
тему?   Пишу,   потому      что
задолбало, завяз   по   уши   в
дебрях    "Черного     Ворона",
хочется подумать  о  будущем ;)
Кстати, в  ZX  Rewю   пробегала
статья, что кто-то  уже  делает
C--  (еще  не  Си, но  уже   не
ассемблер) - может, кто слышал,
что это  за  зверь?

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

subr_ix

PROC (var HL,IX,A) : BC
      GOSUB s1(#5800,A+color)
      .IX[10]=IX[1]+shift[A]-#7
      IFC (IX[10]-100) m1,m2,m3
m1    .IX[10]-=1
      GOTO m2
m3    .IX[10]=(mmx)
      .IX=IX+16
m2    RETURN

   А на ассемблере это было  бы
возможно так (как будто  у  нас
есть  хороший   оптимизатор :


subr_ix PUSH IX
        PUSH AF
        PUSH BC
        LD DE,#5800
        ADD A,color
        CALL s1
        ADD A,(IX+1)
        LD HL,shift
        CALL adr_mas
        SUB #7F
        LD (IX+10),A
        SUB 100
        JR Z,m2
        JR NC,m3
m1      DEC (IX+10)
        JR m2
m3      LD A,(mmx)
        LD (IX+10),A
        PUSH DE
        LD DE,16
        ADD IX,DE
        POP DE
m2      POP BC
        POP AF
        POP IX
        RET

  Хрен чего поймешь. В  смысле,
почувствуйте     разницу      С
горячим приветом, Слава. Все на
разработку народного стандарта!

 05.апр.97




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Мозгомоечная - рекомендации создателям графических редакторов.
Разное - Мысли о конкурсе на лучший софт.
О воровстве - Онтология плагиата: критика графического редактора Excess Deluxe Paint.

В этот день...   24 сентября