Та ну...
В некоторых случаях это единственная возможность...
Так никто и не запрещает.
И что же в Вашем случае он так "удачно перераспределил"?
Вся эта программа компилируется с максимальной оптимизацией. Указывая, какие регистры я задействую, я минимально вмешиваюсь в оптимизацию окружающего кода.
От чего происходит экранирование задействованных во вставке регистров?
Он порчи их содержимого внутри ассемблерной вставки. Если gcc потребуется, он сохранит перед вызовом регистры на стеке. А если не потребуется - оставит без изменений. А учитывая, что код получает ряд переменных просчитанных заранее, gcc вполне может уже на этом этапе хранить эти переменные в регистрах.
Там перераспределять нечего, все регистры, которые можно использовать, оговорены в соответствующих документах на компилятор.
В данном коде можно использовать абсолютно любые регистры, доступные из ассемблера, не заботясь о том, используются они компилятором или нет при условии их перечисления. Всё что нужно, он сам сохранит на стеке в случае необходимости.
Ну-ну, от того, что Вы сами в начале листинга скопировали нужные данные в регистры - чуда не произошло. Вы все равно оперирует регистрами, номера которых нужно либо запомнить, либо постоянно подглядывать. Вот эти строчки:
В данном коде переданные значения были скопированы в 4 регистра исключительно потому, что требовалось поместить их именно в индексные регистры (что, вообще-то можно было бы сделать и явным указанием передать параметр в индексном регистре (как [ras] "x" (RAS), но сделано было на тот момент именно так). А поскольку более значения этих переменных не нужны, то они и не используются. Эта программа не есть демонстрация возможностей встроенного ассемблера в WinAVR. Эта программа решает вполне себе свою задачу.
Кстати, компилятор позволяет чётко отслеживать типы переменных:
[ras_h]"d"(RAS_H), - передаётся в верхнем регистре
[cas_h]"d"(CAS_H), - передаётся в верхнем регистре
[ras_l]"d"(RAS_L), - передаётся в верхнем регистре
[cas_l]"d"(CAS_L), - передаётся в верхнем регистре
[dram_ras]"M"(DRAM_RAS), - передаётся как 8 битная константа
[dram_cas1]"M"(DRAM_CAS1), - передаётся как 8 битная константа
[dram_cas2]"M"(DRAM_CAS2), - передаётся как 8 битная константа
[drive_data]"M"(DRIVE_DATA), - передаётся как 8 битная константа
[drive_side1_mask]"M"(1<<DRIVE_SIDE1), - передаётся как 8 битная константа
[gifr_intf0_mask]"M"(1<<INTF0), - передаётся как 8 битная константа
[pina]"I"(_SFR_IO_ADDR(PINA)), - передаётся как 6 битная константа
[drive_data_port]"I"(_SFR_IO_ADDR(DRIVE_DATA_PORT)), - передаётся как 6 битная константа
[drive_side1_pin]"I"(_SFR_IO_ADDR(DRIVE_SIDE1_PIN)), - передаётся как 6 битная константа
[gifr]"I"(_SFR_IO_ADDR(GIFR)), - передаётся как 6 битная константа
[portd]"I"(_SFR_IO_ADDR(PORTD)), - передаётся как 6 битная константа
[portc]"I"(_SFR_IO_ADDR(PORTC)): - передаётся как 6 битная константа
они по-началу изобилуют растактовкой,
Потому что у программы была предшественница, где указывалась длительность операций. Однако, номер такта и в этой программе сохраняется для того участка, где он важен.
Оформление этого кода во внешнем файле решает эстетическую и логическую составляющие проблемы ассемблерных вставок.
Это, извиняюсь, уже ваше личное мнение.

В данном случае никакой надобности не было оформлять s-файл с отдельным кодом.
При разборе программы постоянно попадающаяся на глаза портянка ассемблерных команд будет мешать.
Да вообще, в данном случае не сильно мешает. Сильно это мешало в GENS 4.51, где текст листать было не очень приятно (да и шрифт мелкий). Но ничего, и с ним справлялись.
К тому же Ваше решение не дает видимых плюсов по сравнению с вариантом написания отдельной функции.
Отдельная функция так же не даёт в данном случае никаких плюсов. Вот когда там будет около 200 строк ассемблерного кода, тогда да - это во внешний файл. А такие крохотные кусочки совершенно незачем выносить куда-то ещё.
Кстати говоря, уважаемый ARV, на утверждение которого Вы так полагаетесь, также упоминает о том, что я говорю:
Это сугубо вопрос вкуса.