I'm wondering if anyone can help me with an annoying issue I face using VASM assembler to compile MC68000 binaries for Amiga. The problem lies in buggy (I think) implementation of labels' addresses manipulations.
Here are the details:
copper_scr:
dc.w $e0, (screen>>16) & $ffff
dc.w $e2, screen & $ffff
...
screen:
dcb.w screen_size ; screen_size value does not matter here
What I'm trying to do in the code above is to split screen address into most significant word and less significant word in order to feed chip registers with screen data address (or vector if you wish).
However, compilation of code in this shape gives me the "illegal relocation" error 39.
I tried many ways to get rid of this, as I supposed that since screen address is long (i.e. not word) the result of "screen>>16" might remain long and therefore I cannot put such a value into word-wide place.
What's interesting, the following code compiles with no errors, but both values in resulting binary are compiled to a value of 0:
...
dc.w $e0,0 + screen>>16 & $ffff
dc.w $e2,0 + screen&$ffff
...
As a temporal dirty workaround I calculate those values at runtime somewhere at the beginning of code:
move.l #screen,a0
move.l a0,d7
lsr.l #4,d7
lsr.l #4,d7
lsr.l #4,d7
lsr.l #4,d7
andi.l #$ffff,d7
move.w d7,copper_scr+2
move.l a0,d7
andi.l #$ffff,d7
move.w d7,copper_scr+6
but that is obviously ridiculous and totally wrong.
Any help appreciated.
The problem lies in the way the assembler (and linker) works:
Some assemblers already know at which address some code will later be placed while other assemblers write object files and the linker will decide where the data will be placed.
In the object file an instruction like dc.w 1234 + screen>>16 & $ffff
will be stored as dc.w 1234
and an additional information is stored that the high 16 bits of screen
must be added to 1234
as soon as the address of screen
is known.
Unfortunately there are two problems:
Not all architectures support all types of information. Object files for Sparc CPUs for example support an information "add the low 10 bits of an address to a value" (because such CPUs have instructions using the low 10 bits of an address) while object files for m68k do not support the "low 10 bits of an address" information type.
Unfortunately the "high 16 bits of an address" information type is also not supported by m68k object files. (At least not if you use the GNU tools - I'm not sure about VASM.)
Assemblers are stupid. They will not detect that screen>>16 & $ffff
is equal to "the high 16 bits of an address". So even if your file format (PowerPC object files, for example) supports that type of information the assembler will have a problem.
Possible work-around:
If you have a label in the same section and you definitely know the address of that label you can do the following.
Let's assume you know that the label xyz
will be loaded into address $1234
in the memory later.
Now you can do the following:
xyz:
...
dc.w $e0, 0 + (screen - xyz + $1234) >> 16 & $ffff
...
screen:
However if you don't know the "final" address of any label you'll have a problem...
Nice workaround could be:
move.l #screen,d0
move.w d0,scrptr_low+2
swap d0
move.w d0,scrptr_high+2
...
scrptr_high: dc.w $e0,0
scrptr_low: dc.w $e2,0
or
scrptr: dc.l screen
...
move.l scrptr,d0
<then the same>
This way the linker and exe-loader-relocator will deal with the usual 32-bit addrresses which they know how to relocate properly.