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
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября