Bruce Dawson Review
22 messages in this thread
My main complaint with the Aztec assembler is that you can't create branch
tables and have the assembler flag errors if something moves out of range. IE,
if you have a list of "bra.s someplace" statements, and one or more go out of
range, Aztec will just make that one into a bra.l, which creates havoc if you
use jmp xx(pc,dn.w) to do a switch. With CAPE, if you say "bra.s" and a branch
goes out of range, it generates an error message.
…cheath
Hi charlie. Do you use the Aztec assembler from the "C" package of is there a
seperate (and different) program? I had some trouble using the Manx assembler
with the 1.3 relese.
Yes, I use the MANX AS from the C package … but I've still got V3.4, have not
updated to V3.6 yet. Have you tried running without Shell-Seg? There may have
been some changes in the BCPL linkages with the new shell, which might collide
with the way Jim handles processes. I'd think other folks would have noticed
this by now though.
Hum I hav indeed had trouble with the 1.3 Shell and programs written for or
rather with Manx C. Im told a new version slated for Jan will take care of
that and (I hope and pray) Time().
Not sure I follow the reference to "jmp xx(pc,dn.w)" creating havoc. The
proper way to do this would be to vector off of an address picked up from a
label table. In this manner the offsets are always correct regardless of what
the assembler or YOU might change. It looks as if you're loading up a
precalculated offset or somesuch, which is asking for trouble. Or are you
talking about phase errors during assembly?
Completely confused,
Rick
THe problem is that the table being jumped into by jmp xx(pc,dn) expects all
short branches, but with Manx AS, no error is generated if an explicit "bra.s"
instruction actually assembles into a "bra.l" instruction. This throws the
table off. For example:
jmp mylab(pc,D0)
mylab:
bra.s Func1
bra.s Func2
bra.s Func3
Here, if the bra.s Func2 instruction actually becomes bra.l without warning,
when you try to execute "bra.s Func3" you actually wind up trying to execute
the word offset "Func2".
…cheath
Aha. Well that's the problem then; you're doing it wrong. 8) I will point out
that even if the assembler DIDN'T hink with your branches you'd have the same
problem. If one of your branches was OOR you'd have to change it to a long
yourself, and then you can't use a calculated offset into the table, because
the entries are of different sizes (unless you make them ALL longs). Blech. No,
make that double blech.
Much better is the approach I suggested in the previous message: the one I
THOUGHT you were using. Never, EVER build a table of branches, that's simply
asking for trouble (as you have pointed out). Instead, build a table of
ADDRESSES. Index into the table (which will always remain orthogonal), pick up
the address, then do an indirect jump, branch, or call as needed. Using your
example it would be something like:
LSL #2,D0 ASSUMES D0 CONTAINS BASE ZERO ORDINAL OF VECTOR
LEA MYLAB,A0 LOAD BASE OF VECTOR TABLE INTO A0
MOVEA.L 0(A0,D0.W),A0 PICK UP ADDRESS OF ROUTINE TO USE
JMP (A0) JUMP INDIRECT OFF OF A0
MYLAB:
DC.L FUNC1,FUNC2,FUNC3,…FUNCN
There are a zillion ways to code it, of course… this one is intended for
clarity.
<continued>
That way is fine if you need longword jumps, but it takes up twice as much
space as a short branch table if you can do the switch with short branches.
With CAPE, you can build short branch tables without having to review the code
generated, because it will tell you if any entry in the table has gone out of
range. It's also possible to do something like you're doing but with a
word-offset table rather than using full longword addresses, if everything is
within +/- 32K bytes of a base address.
…cheath
Right, but with word addresses you have the same problem, I.E., if one of them
won't fit into a +/- 32K range, you've got to stick in a longword, which blows
up the table, or insert an island branch somewhere. This approach is
absolutely, completely foolproof.
As far as size is concerned, if I was writing code for an embedded control
system with limited ROM (which I happen to be doing at the moment, but that's a
whole 'nother story), I'd keep an eye on it. But good heavens, even if you
have a hundred entry vector table (which would be huge), we're only talking
about an overhead of two hundred bytes here. (If you compare it with short
branches, that's STILL only 300 bytes.) In even a lil' 512K machine 200 bytes
is trivial. AND if it comes down to it, I'd wager a good programer could find
far more than 200 bytes wasted in a progam large enough to require a 100 entry
vector table. Here, change those modules you are JUMPing to to subroutines and
CALL them with longwords. Now the end of each one returns to just after the
vectored jump, and you can then swing back into your main loop. A RTS is one
byte shorter than a short branch; tada… there's 100 bytes right there. I'll
leave it to you to find the other byte. 😉
I'd rather squander space on solid, reliable code and clean up dregs elsewhere.
(Which I'm sure you would as well; I'm not accusing you of anything.)
Rick
Yes, but using bra.s or word offsets can save you about 20 bytes or so of
relocation info per reference. a hundred entry vector table is actually costing
you many more disk bytes, that's something to consider also.
Scott, I'll have to ask you to explain your comments about relocation info and
disk space, please?
Rick
With branches or word offsets from a table base don't have to be relocated on a
load, but a table of 32bit absolute addresses has to be relocated by the
loader, so the overhead is something like hunkmarker + 4bytes for zero end
marker + hunkend + 8 bytes for hunk number and count which gives 24 bytes for
the first reference, and then you have another 4 bytes for each additional
reference, so a 100 address table would occupy 800+20 bytes on disk, which
compares with 200 bytes for a word offset table, which is a pretty big savings
in disk space, although once in memory the difference is much less, as you
pointed out.
sdb
Ah, I hadn't thought of that; I see what you mean now.
This is typical of why I enjoy hanging out here. Geez, if you think you don't
have anything to learn, don't toss bits with THESE guys. 😉
Rick
Rick,
A RTS is one byte shorter than a short branch? You must not be programming the
Amiga — they're both 2 bytes long on the 68000, last time I checked.
Bill
<URP!> You're wrong and right. I AM programming the Amiga — among other
machines. But the 68000 RTS *IS* two bytes (4E 75, to be exact). Every other
CPU I program has a byte-wide RTS, and I forgot the 68000 was different in this
regard. Thanks for keeping me honest.
On the OTHER hand, if we assume the code segments being vectored to are too far
away from the re-entry point to be reached with a short branch (which is why
Cheath's table approach blows up in the first place), then the jump back would
be FOUR bytes long, not two… and now we've recovered all 200 bytes. 8)
Rick
Rick,
When I set up jump tables I usually try to use BYTE size relative offsets, as
the savings in the table nearly always makes up for the extra instruction to
load the offset before adding it to the jump address. This lets me make
forward references of up to 256 bytes before overflowing the operand.
Bill
Rick,
Here's how I handle it assuming all references fit into a single source
file.
The code fragment:
lsl.w #1,d0 ;multiply index by 2 for table offset
lea cmdtable(pc),a0 ;get address of table
move.w 0(a0,d0.w),d0 ;get routine offset
adda.w d0,a0 ;calculate routine address
jsr (a0) ;jsr to routine
The table: cmdtable:
dc.w (FUNC1 – cmdtable)
dc.w (FUNC2 – cmdtable)
…
The advantage of this is that there is no relocation data needed in the
load file, which makes the code directly ROMable and self-relocating. If you
for some reason need a long offset, the assembler will let you know by telling
you the value is too large to fit into the defined word. You can then fix it by
simply doing a S/R for a 'dc.w' and replace with a 'dc.l', and finally changing
the 'lsl.w #1,d0' to a 'lsl.w #2,d0'.
-Dean
Looks like a nice clean approach, Dean. I'm going to keep it in mind for the
next time I have to code such a table. Thanks.
Rick
Sure, and if you like you could change every bra.s in your program into a jmp.l
#nnnn, and it would only cost you four bytes for each time you do it. But why
are you bothering to write in assembler if you don't want smaller & faster
code? Using a bra.s table is 100% reliable if you have an assembler that
generates an error if you go out of range.
…cheath
Re your comments on the reliability, granted… IF your assembler doesn't hink
with your code. But if it does, surprise! Of course we both realize that this
isn't an important issue… it's back to the old saw of as many was to code as
there are coders, eh?
Rick
I guess the most important thing for me is that I know what my tools are doing,
and preferably that they don't leave holes that are easy to fall into. With
CAPE I can trust that it's doing what I want it to do to my source code, though
I wish it would optimize forward references to short branches. If I saY
"bra.s" or "bra.l", I want the assembler to do exactly that, but if I say "bra"
I'm asking it to do optimization for me.
…cheath
<continuation>
We should both acknowledge that this is a side issue seperate from assembler
design. On THAT point, I acknowledge that I'd feel uncomfortable about an
assembler doing ANYTHING to my code that I don't know about. Compilers, okay;
assemblers, no. Give me a switch so I can enable/disable branch optimization
and the like or don't give it to me at all. (I've coded all my 68000 code
using explicit .B/.W./.L extenders on EVERYTHING. I aways assumed it was being
left alone. Now I begin to wonder. Maybe I've just been lucky?)
Rick