AlexanderZh wrote:Я бы тут поспорил, не всегда используются константы, иногда действия нужно провести с переменными.
P.S. Я ассемблер знаю...
Тут не важно каким инструментарием реализован алгоритм процедуры разбора выражений. Это м.быть как подпрограмма на ассемблере, так и процедура на Паскале или Си. Я использовал ассемблер, лишь потому, что тогда ассемблер уже знал, а ЯВУ ещё нет.
Не понял о каких переменных идёт речь. О переменных программы (а это просто байты в ОЗУ для которых задана адресная метка) или о переменных ассемблера. Они в отличие от констант, что однократно задаются оператором EQU могут меняться оператором SET многократно. Для ассемблера нет переменных программы, для него они просто адресные метки, т.е просто проименованные двухбайтовые числа занесённые в первом и втором проходах в таблицу меток. Т.е в ассемблерах метками считаются все проименованные числа, что хранятся в таблице меток - реальные адресные метки, константы и переменные (что по сути те же константы, но которые можно изменять в ходе трансляции и в зависимости от их значения менять поведение компилятора или ход трансляции).
И конечно речь идёт о полноценном ассемблере, а не о примитивном, потому под выражением понимается любое выражение содержащее как числа (DEC, HEX, OCT, BIN), константы (они задаются по EQU), адресные метки, так и переменные (они задаются оператором SET и по ходу трансляции могут меняться).
Т.о при вычислении выражения процедура вычисления выражения "знает" адрес начала таблицы меток (её структура: 6 байт имя, 2 байта значение, т.е шаг 8 ), в которую кроме адресных меток, также занесены и константы заданные по EQU и переменные заданные по SET. Для ассемблера все эти именованные числа совершенно равнозначны. Узнав (после вызова подпрограммы LEXEMA), что следующая лексема это метка, константа или переменная, ассемблер начинает сканировать таблицу меток в поисках 6-ти совпадающих байтов имени, и найдя, просто извлекает из таблицы двухбайтовое значение.
Вопрос о том, чем плох линейный алгоритм вычисления выражений?
Имеется текущий RESULT, встретив лексему "знак операции", например оператор MOD, выполняется (табличный) переход на процедуру вычисления "взятия выражения по модулю". Эта процедура, имея на входе указатель по строке указывающий на байт следующий за словом MOD, считывает из строки число (или рекурсивно вычисленное выражение), берёт текущий RESULT, делает вычисления над этими двумя числами (в случае операции MOD делит RESULT на число, считанное из строки, и полученный остаток от деления помещает в RESULT) и делает возврат из процедуры обслуживания оператора MOD.
Как видите, какие бы ни были знаки операций (арифметические, битовые или логические) нет никакой нужды в стеке данных (до тех пор пока не используется вложенность скобок). Линейный алгоритм, мне кажется, должен быть проще, чем хитроумно преобразовать строку в какую-то странную обратную польскую запись, а затем стеково вычислять это выражение. Или строить сложное
дерево. Эти алгоритмы
очень сложно даже объяснить словами. А ещё их сложно сделать на ассемблере.
На всякий случай уточню. Под стеком данных имеется ввиду вовсе не аппаратный стек процессора (речь же не о форте). Аппаратный стек в ассемблере занят для обычных целей, как в любой программе (для хранения адресов возврата из подпрограмм). Стек данных организуется программно, для чего есть две подпрограммы PUT и GET (аналоги PUSH и POP, но эти имена для меток в ассемблере запрещены). Кстати, для процессоров у которых два стека (обычный стек программы и стек пользователя) стек данных может быть аппаратным.
При вызове PUT число из HL заносится в программную структуру имеющую шаг в 2 байта и указатель стека (программный, т.е просто двухбайтовое число под меткой UKAZSP) сдвигается на 2 адреса. При вызове GET делается обратное и в HL возвращается значение из стека данных.
Стек данных нужен для того чтобы встретив открывающую скобку, где-то запомнить текущий результат вычисления выражения и следующий знак операции, чтобы освободить переменные процедуры вычисления выражений для новой рекурсии. Кстати, знак операции в ассемблере это не обязательно один символ, например: AND, XOR, EQ, LT, SHR, HIGH. Потому в стеке сохраняется не символьная строка, а соответствующий однобайтовый код (возвращаемый процедурой LEXEMA).
Кстати, тут видно, что аппаратный стек удобнее иметь двухбайтовым, как это сделано во всех процессорах, кроме 8-ми разрядных моторолла-производных (6800, 6502, 6809). Из-за этого компиляторы для этих процессоров оказываются более громоздкими, а генерируемый ими результирующий код - менее эффективным.
P.S. Был бы благодарен, если бы кто-нибудь дал мне готовый REL-модуль прогрессивной процедуры вычисления выражений. Могу дать в обмен свой аналогичный REL-модуль, который видимо уже в ближайшие годы доработаю до вложенности скобок.
- - - Добавлено - - -
В принципе, тут речь не об кроссассемблере, а вообще о любом ассемблере. Поместил 2 поста сюда, т.к вопрос о стеке и всё-равно тут обсуждение и до меня "заоффтопилось" на компиляторы КР580 на родной платформе. Логично было бы эти мои посты поместить в тему просто "
Компиляторы ассемблера".
Но сейчас тема "
Компиляторы ассемблера" заполнена обсуждениями не ассемблера, а наоборот,
кроссассемблера, причём 4-х разрядного (что как бы немножко иная тема, чем ассемблер для 8-ми разрядной ретро-ЭВМ, что по умолчанию подразумевается исходя из основной тематики форума). Потому если добавить туда эти 3 поста, то возникнет "салат", неудобный для чтения.
Если хотите, то можно перенести 3 последних поста в тему "Компиляторы ассемблера", убрав оттуда обсуждение 4-х разрядного кросс-ассемблера (в данную тему или в отдельную).
Есть ещё неудачный вариант выделить отдельную тему "Вычисление выражений в ассемблере и бейсике" и настрогать туда вырезки постов из всех тем, где обсуждается вычисление выражений. Хотя непонятно тогда, что обсуждать в темах про сами ассемблеры и эти темы станут почти пустыми.
И раз уж 80% этой темы уже сползло от темы кросс-ассемблеров 8080 на просто ассемблеры 8080, м.быть разумно сменить название темы на "Кроссассемблеры и ассемблеры для 8080" или "8080 (кросс)ассемблеры" или "Инструменты для программирования на ассемблере 8080".
А вообще эта тема очень давно родилась как вопрос "где скачать кросс-компилятор?". Но этому посвящены лишь посты, что идут в самом начале темы. А затем разговор переключился на другое "как писать компилятор", какие общепринятые соглашения, каковы алгоритмы отдельных процедур внутри компилятора.
Может быть разумно сделать две темы: "Где скачать кросс ассемблер 8080" (пополнив тему новыми ссылками) и тему "Компилятор ассемблера 8080" или даже "Некоторые аспекты написания компилятора ассемблера" или что-нибудь подобное.