AVR, Сборка, Флаг V

Нужен совет по программе на ассемблере (AVR ATMega169). Мне нужно создать код для вычисления этого выражения.

R20 = (4 * R16 + 3 * R17 - R18) / 8

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

  .org 0
start:
    ldi r16, 10
    ldi r17, 20
    ldi r18, 10
        
    lsl r16
    brvs OVERFLOW

    lsl r16
    brvs OVERFLOW

    mov r20,r17
    brvs OVERFLOW

    lsl r20
    brvs OVERFLOW

    add r20,r17
    brvs OVERFLOW

    add r20, r16
    brvs OVERFLOW

    sub r20,r18
    brvs OVERFLOW

    asr r20
    brvs OVERFLOW

    asr r20
    brvs OVERFLOW

    asr r20
    brvs OVERFLOW
    
    
    brvc OK

    OVERFLOW:
    ldi r30, 0x11

    OK:
    ldi r30, 0x11
konec:
    rjmp konec

person martin12888    schedule 21.04.2021    source источник
comment
Не зная лучше, мы должны рассматривать уничтожение одной из входных переменных как дурной тон. Итак, вы должны скопировать r16 куда-нибудь перед учетверением. Кроме того, вы не суммируете этот расчет 4 * R16 до деления на 8.   -  person Erik Eidt    schedule 21.04.2021
comment
Вы хотите иметь дело с переполнением при умножении, а также при добавлении/вычитании? будучи подписанным, если знак меняется, то сдвиг влево - это переполнение, да? и для сложения/вычитания, которое выполняется аппаратно, я надеюсь, что в противном случае его также не сложно рассчитать, в чем здесь настоящая проблема? можете ли вы лучше объяснить или показать пример переполнения, который вы не обнаруживаете?   -  person old_timer    schedule 21.04.2021
comment
Что именно вы имеете в виду, не может быть рассчитано правильно? Окончательное значение этого выражения гарантированно помещается в 8-битный регистр, между -127 и +127 для 8-битных входных данных со знаком, поэтому выбор метода вычисления, который может привести к переполнению промежуточного значения, зависит от вас. Вы можете просто использовать дополнительную точность (2 регистра) для промежуточных значений, где это необходимо, вместо проверки переполнения после каждой инструкции lsl / add / sub.   -  person Peter Cordes    schedule 21.04.2021


Ответы (1)


Что касается правильной установки флага переполнения, то большинство отдельных операций будут правильно устанавливать флаг переполнения, а именно add, sub и lsl. Однако asr, по-видимому, не устанавливает флаг переполнения особенно осмысленным образом в отношении деления со знаком.

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

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

Другим подходом было бы or флаг переполнения после каждой операции в некоторую переменную, а затем использовать эту переменную для установки флага переполнения в конце. Этот подход оставляет более предсказуемое значение в R20, когда происходит переполнение, за счет дополнительных инструкций и переменных.

person Erik Eidt    schedule 21.04.2021
comment
Итак, если бы у меня были только add, sub .. в расчетах, я мог бы использовать флаг V для каждой отдельной операции, пока не наткнусь на asr? А можно поинтересоваться, как еще проверить переполнение кроме инструкции BRVS/BRVC? - person martin12888; 21.04.2021
comment
Я не знаю, как еще проверить флаг переполнения на этом процессоре, кроме как для перехода. Похоже, если вам нужно значение в регистре, вы должны использовать некоторую форму if-then или if-then-else. - person Erik Eidt; 21.04.2021
comment
Можно поинтересоваться, как бы выглядела ветка на ассемблере или где ее изучить? Я немного теряюсь в нем? - person martin12888; 21.04.2021
comment
Не уверен, что ваш вопрос. Ассемблерный/машинный код использует стиль if-goto-label для управления потоком, например if-then, while-loop, for-loop. Для инструкции условного перехода, такой как BRVS, требуется только один операнд, которым является метка. (Флаг переполнения подразумевается в коде операции.) Во время его выполнения процессор изменит программный счетчик (который сообщает ему, какую инструкцию выполнять следующей) условно на основе значения флага переполнения: следующий последовательный адрес инструкции, иначе он изменит компьютер, чтобы он был равен целевому адресу перехода. - person Erik Eidt; 21.04.2021
comment
Я, наверное, плохо выражаюсь по-английски. Потому что дело в том, что я понятия не имею, как проверить, что переполнения не произошло и что результат правильный. - person martin12888; 21.04.2021
comment
Если переполнения не происходит ни в одной отдельной операции, то переполнения не произошло и в более крупном вычислении. Иными словами, если переполнение происходит в какой-либо из отдельных операций, то весь расчет можно считать переполненным. - person Erik Eidt; 21.04.2021
comment
Вот почему я предлагаю проверять переполнение после каждой операции, возможно, путем перехода к метке, которая может сообщить об этом, или просто до конца вычисления. Ветвление где-то при переполнении не очистит флаг переполнения, поэтому, когда программа попадет туда, флаг переполнения все еще будет установлен на этом пути, и это, похоже, то, что вы ищете. Идея состоит в том, чтобы просто остановить вычисление, когда установлен флаг переполнения, поэтому вы переходите к концу с этим установленным флагом вместо того, чтобы делать фактический ответ в R20. - person Erik Eidt; 21.04.2021
comment
Как я уже сказал, по этой логике, если программа достигает деления на 8, то пока не произошло переполнения. Разделение на 8 не может переполниться, как и asr; затем снимите флаг переполнения как индикатор того, что ответ верен. - person Erik Eidt; 21.04.2021
comment
@ martin12888: BRVS handle_overflow после каждой инструкции является тем, как вы пишете ветку/проверку. - person Peter Cordes; 21.04.2021
comment
@ErikEidt: Чтобы ИЛИ флаг V был результатом каждой операции, лучшим способом может быть загрузка SREG в регистр и ИЛИ в другой регистр (после каждой операции). (Регистры AVR отображаются в памяти). Итак, 2 инструкции на операцию вместо BRVS. Когда вы закончите, я думаю, сохраните обратно в SREG, поэтому каждый флаг обновляется до ИЛИ любого результата во время вычисления. - person Peter Cordes; 21.04.2021
comment
Может быть меньше работы, чтобы вычислить все выражение таким образом, чтобы исключить переполнение, например, с 16-битным сложением и сдвигом вправо с 2 регистрами. Окончательное значение этого выражения гарантированно помещается в 8-битный регистр между -127 и +127 для 8-битных входных данных со знаком. А может и нет, добавлений/смен не так много. - person Peter Cordes; 21.04.2021
comment
Я внес коррективы в код, где я проверяю его после каждой инструкции, но я не думаю, что он работает должным образом, потому что в этом состоянии не должно происходить переполнение (если я не проглядел), я должен использовать только 8-битный в программа - person martin12888; 21.04.2021
comment
но тогда для этого asr я должен удалить его? - person martin12888; 21.04.2021