VASM cross-assembler issue (m68k)

2019-07-31 17:03发布

问题:

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.

回答1:

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...



回答2:

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.