У меня при компиляции проекта, содержавшего смешанный код (ассемблер и C - это был проект с использованием библиотеки V-USB. Ассемблер был нужен для кода, критичного к времени выполнения), происходила следующая ошибка:
Error[e18]: Range error,
PC offset out of range. Valid range is -4096 (-0x1000) to 4094 (0x0FFE).
File: C:\asm\AVR910-protoss\usbdrv\usbdrvasm.S, Line: 54
Source: rjmp USB_INTR_VECTOR
Where $ = __ATmega128__ + 0x4 [0x4]
in module "usbdrvasm" (c:\asm\AVR910-protoss\Debug\Obj\usbdrvasm.r90),
offset 0x4 in segment part 0, segment INTVEC
What: (usbCrc16 + 0x32) - ($ + 2) [0x1182]
Allowed range: 0xFFFFF000 - 0xFFF
Operand: usbCrc16Append [0x1188]
in module usbdrvasm (c:\asm\AVR910-protoss\Debug\Obj\usbdrvasm.r90),
Offset 0x32 in segment part 1, segment CODE
Проблема была в том, что команда rjmp USB_INTR_VECTOR в таблице векторов прерываний не доставала до нужной её метки (адрес метки был слишком большой для перехода по rjmp).
Метода устранения ошибки два:
a) Заменить команду rjmp на команду jmp - у неё нет тех ограничений, по дальности перехода, как у rjmp, но выполняется jmp на 1 такт дольше. В моем случае это нежелательно - код критичен к времени выполнения прерывания.
b) Поместить код, который находится по метке USB_INTR_VECTOR, ближе к началу памяти, чтобы достала команда rjmp.
Второй способ можно реализовать, добавив перед кодом с меткой, куда прыгает rjmp, директиву назначения сегмента, который размещается линкером ближе к началу памяти, чем сегмент CODE. Порядок сегментов, которого линкер по умолчанию придерживается, следующий (подсмотрел в листинге *.map):
-Z(CODE)INTVEC=0-(_..X_INTVEC_SIZE-1)
-Z(CODE)TINY_F=_..X_FLASH_BASE-FF
-Z(CODE)NEAR_F=_..X_FLASH_BASE-_..X_FLASH_NEND
-Z(CODE)SWITCH=_..X_FLASH_BASE-_..X_FLASH_NEND
-Z(CODE)DIFUNCT=_..X_FLASH_BASE-_..X_FLASH_NEND
-Z(CODE)CODE=_..X_FLASH_BASE-_..X_FLASH_END
-Z(CODE)FAR_F=[_..X_FLASH_BASE-_..X_FLASH_END]/10000
-Z(CODE)INITTAB=_..X_FLASH_BASE-_..X_FLASH_END
-Z(CODE)HUGE_F=_..X_FLASH_BASE-_..X_FLASH_END
-Z(CODE)TINY_ID=_..X_FLASH_BASE-_..X_FLASH_END
-Z(CODE)NEAR_ID=_..X_FLASH_BASE-_..X_FLASH_END
-Z(CODE)CHECKSUM#_..X_FLASH_END
Самый лучший кандидат на нужный сегмент для размещения кода по метке USB_INTR_VECTOR - сегмент NEAR_F. Ошибка пропала, когда я размести перед этим кодом директиву RSEG NEAR_F, вот так (метка USB_INTR_VECTOR как раз та, которая стоит в команде rjmp):
RSEG NEAR_F
USB_INTR_VECTOR:
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt
push YL ;2 [35] push only what is necessary to sync with edge ASAP
in YL, SREG ;1 [37]
push YL ;2 [39]
..