2016-11-29 6 views
5

Используя Arduino, я должен написать функцию в сборке Atmel AVR для моего класса информатики, которая преобразует подписанный 8-разрядный байт в 16-битное целое число. Мне также не разрешено использовать какие-либо инструкции по ветвлению (но пропуски прекрасны).Как преобразовать подписанный 8-разрядный байт в 16-битное целое число в сборке?

Я знаю, что это неправильно, но это все, что я получил до сих пор:

.global byteToInt 
byteToInt: 
    sbrc r24, 7 
    ldi r25, 1 
    asr r25 
    ret 

Кто-нибудь знает, как я хотел бы сделать эту функцию работать? Любая помощь приветствуется!

+1

Какой формат для подписанных номеров учитель неявно предполагает? Два дополнения? –

+1

@MargaretBloom: флаг знака AVR выглядит так, как будто он установлен на основе интерпретации дополнений 2-х, поэтому я думаю, что это само собой разумеется. Кроме того, [инструкция NEG] (http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_NEG.html) делает отрицание дополнений 2. –

+0

@PeterCordes Eh, Может быть, или нет. Разумно предположить, что это дополнение к курсу, но упражнение может состоять в реализации других подписанных представлений (именно из-за отсутствия поддержки со стороны ISA). Я тоже предположил, что это дополнение. –

ответ

3

Ну, очевидно, вам нужно скопировать бит знака char на каждый бит в верхней половине. На большинстве архитектур проще всего скопировать регистр и арифметико-правое смещение на 7. Но AVR имеет только shift-by-1 instruction, поэтому мы не можем эффективно это делать.

Другой уловкой для условного получения 0 или -1 в регистр является subtract-with-borrow регистр от себя, чтобы получить 0 - C. например sbc r25, r25.

Теперь нам нужен способ установить флаг Carry, если 8-разрядное число отрицательное, т. Е. Если оно> 127, когда рассматривается как целое без знака, потому что C всегда задается на основе неподписанной интерпретации вещей. AVR имеет команду сравнения-немедленного действия, CPI, но она работает только для r16-r31, а не для низких регистров. Кроме того, он устанавливает флаг C напротив того, что мы действительно хотим, поэтому нам пришлось бы использовать другую инструкцию для инвертирования результата. Так что я думаю, что мы лучше делает сравнение другой путь против значения в регистре:

; Most efficient way, I think: 
sign_extend: 
    ldi r25, 127  ; can be hoisted out of loops, and any reg is fine. 

    cp r25, r24  ; C = (r24 < 0) 
    sbc r25, r25  ; r25 = (r24 < 0) ? -1 : 0 
    ; result in r25:r24 

Еще лучше, если вам нужно сделать, это в цикле, вы можете сохранить 127 в другом регистре.

С ИВК, вы могли бы сделать это:

; slightly worse: only works with r16-r31, and worse in loops 
sign_extend: 
    cpi r24, 127  ; C = (r24 < 128U) = ((signed)r24 >= 0) 
    sbc r25, r25  ; r25 = (r24>=0) ? -1 : 0 
    com r25    ; ones-complement negation: 0 : -1 

Или, чтобы избежать ограничений, на которых используется регистр, сделайте сравнение другой путь:

Я никогда не работал с AVR , поэтому я просто основываю это на справочном руководстве по набору инструкций, которое найдено google (и мои знания asm для других ISA, таких как x86 и ARM). Согласно этим документам, все эти инструкции являются 1 словом (2 байта) с 1 задержкой цикла. Это лучше, чем то, что делает gcc4.5:


Обычный способ найти хорошие последовательности инструкций является задать компиляторAVR gcc4.5 -O3 on godbolt делает это:

short sign_extend(signed char a) { return a; } 

sign_extend: 
    mov r18,r24  ;; IDK why gcc uses r18 and r19. 

    clr r19 
    sbrc r18,7 
    com r19 

    mov r25,r19 
    ret 

Так zeros R19, а затем использует SBRC для условного выполнения логического-not (COM), в зависимости от бита знака (бит 7) R18.

Я не уверен, для чего нужны дополнительные MOV. Я также не уверен, почему он инвертирует нуль, а не устанавливает все биты без зависимости от ввода. (например, ldi r19, $FF или SBR alias for it.Если когда-либо существовал AVR вне очереди, это было бы более эффективно: P

Я не уверен, что для инструкций MOV. SBRC - разрушительный.Так AFAICT, действительное осуществление будет

sign_extend: 
    clr r25 
    sbrc r24,7 
    ldi r25, $FF 
    ret 

Это еще хуже, чем CP/SBC, поскольку SBRC занимает 2 цикла, если пропуск берется.


Я предполагаю, что «ложная зависимость» SBC от старого значения R25 не является частью AVR. На процессорах x86 с невыполнением заказа только AMD распознает sbb eax, eax как независимое от старого значения eax и зависит только от флагов. Процессоры Intel просто запускают его нормально. (Они признают инструкции, как xor eax,eax как независимый и it's the standard zeroing-idiom for x86.)

Так что на не-процессоры AMD, если последний код, который написал EAX сделал это с грузом, который пропустил в кэше, или что-то еще с большой задержкой, sbb eax, eax не удалось выполнить, даже если флаги были готовы (т. е. из независимой цепочки зависимостей). Но на процессорах AMD он начнет новую цепочку зависимостей для EAX.

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

+0

Большое вам спасибо! У меня возникли проблемы с получением -1 в реестре, я не думал о том, чтобы сделать вычет-с-заем после сравнения; это очень облегчает жизнь. – stealthbomber10

+2

@ stealthbomber10: Да, это удобно, когда трюки, которые я знаю из одной архитектуры (x86), полезны для другого (этот ответ). x86 даже имеет инструкции для [установить регистр на 0 или 1 на основе флага] (http://www.felixcloutier.com/x86/SETcc.html), но они работают только с 8-битными частичными регистрами и равны 0/1 не 0/-1. (Конечно, инструкция DEC превращает 0/1 в -1/0, это еще один полезный трюк). Один из моих любимых мошенников заключается в том, что AMD не изменила SETCC для записи полного 32-битного регистра в 64-битном режиме (с обычным неявным нулевым расширением до 64 бит); это было бы более полезным и эффективным. –

+1

@ stealthbomber10: Маргарет отправила ответ, который использовал 'lsl r24', чтобы получить бит знака в CF, а затем' ror r24', чтобы вернуть его. Она удалила его, решив, что это не такая хорошая методика, как сравнение, но это хороший пример того, что обычно есть несколько способов получить флаг, установленный бит. (даже если это должен быть определенный флаг, например, C). –

 Смежные вопросы

  • Нет связанных вопросов^_^