need asm mavens' wisdom
25 messages in this thread
There are often more than one way of doing the same thing in assembly
language. What's the best way to choose between them. Two examples:
I recently added the following somehow unprofessional looking bit of code
to a program I am working on:
move.b #0,(a5)+
move.b #0,(a5)+
move.b #0,(a5)+
move.b #0,(a5)+
move.b #0,(a4)+
move.b #0,(a4)+
move.b #0,(a4)+
move.b #0,(a4)+
Theoretically I might pad the memory at A4 and A5, and use move.w or
move.l, but I would rather not. Another possibility would be something
like the following (which I haven't actually tried, so there may be a bug
in it)
moveq #3,d0 loop: move.b #0,(a5)+
move.b #0,(a4)+
dbra.s d0,loop
That looks more like "programming," but I am afraid it would take maybe
three times as long, and I'm in a bit of a hurry at this point in the
program.
Another case in point is the following two statements. It seems to me they
will both have the same effect (lets asuume that status flags are
irrelevant). How does one figure out which is faster (or otherwise
preferable) in a case like this?
andi.b #$7f,d2 vs.
bclr.b #7,d2 (I started with the andi, but I'm inclined to switch
to the bclr…?)
– via Whap!
It seems like you have all the answers already: sometimes you want to
optimize for speed, and want to avoid loop tests. Sometimes you don't want
to waste 30 bytes doing something when 5 bytes can do it more slowly.
Compiler code generator writers worry about such things all the time, where
a program generates the assembly code. Sometimes compiler writers want to
appeal to speed tests, sometimes to total code size. You might be surprised
if you use a disassembler to view how other programmers answered this
question: some programs have *miles* of unrolled loops.
Thanks, especially for suggesting disassembling some programs to see what
others do. I haven't tried disassembling since about the time I was just
starting to learn assembly language "for real," and scared the hell out of
myself.
I wonder if I'm ready to look at MEmacs and FutureSound yet?
– via Whap!
You might NOT want to do a lot of disassembly. If you do you might get the
idea that this or that is the way that its supposed to be. Make up \your
own mind (at first) and form some opinions. Then try to find some
different code and REMEMBER!!! Question authority! <grin>
I got a half-dozen samples of four different people's programming with my
assembler. Then I bought Abacus' _Amiga Machine Language_. Each person's
style (?) is so completely different from everyone else's that I think I am
forever cured of the idea that there is one right way to do something.
(Although I suspect that once assembled, the object codes wouldn't look
nearly as different as the source codes do.)
Still, you're right. I DON't want to do a LOT of disassembly. Neither
of the disassemblers I have does commenting worth a damn! 🙂 But for
some of the things I want to do, I'm going to have to compare methods with
people who have done similar things. Or read books.
I very much appreciate your advice.
– via Whap!
Bart,
I agree with Jay's advice about looking at other people's programs. In my
time I've seen code that is absolutely appalling. The best advice is to
keep it simple, keep the code in small chunks. If you've got to jump
around, don't jump more than a few lines. If a routine takes up more than
a page, it's too long.
Also remember, most code you see in books is written by academics, and bear
in mind the good old saying "Those who can, do. Those who can't, teach."
And try to be helpful to the person (you?) who is going to be trying to
debug your code in a year's time.
Regards,
Shraddhan – via Whap! from Hertfordshire in the UK
My advise is don't worry how it "looks" and TRY to avoid overly cute
programs. This alone will help you a LOT! I think that the first example
is just fine. I couln't be much more clear as to what you are doing. And
as far as taking up program space… Who cares! Thats what so nice about
68K machines. You don't have so much byte counting to keep everything in
its segment etc.
Bart,
If you can _guarantee_ that A4 and A5 are pointing to even addresses, your
best bet is to use move.l #0(a5)+ etc. (20 clock cycles per 4 bytes, as
compared to 12 cycles per byte) I don't know why you'd rather not pad the
memory to achieve this – what's a byte, after all?
Your use of a loop, as you say, will be much slower. Can your assembler
handle repeat loops? On mine, I can do:
REPT 4
move.b #0,(a4)+
move.b #0,(a5)+
ENDR
This _looks_ like a loop, but will generate code much like your original
version. Personally, I prefer writing it out in full in this case, to make
it clearer that I could not use a more efficient technique.
As for the relative speed of ANDI vs. BCLR – my book is ambiguous (due to
bad printing). If the difference was important to you, you could write a
loop to execute the instruction a large number of times, and time it with a
watch.
I would be inclined to use the BCLR approach if the bit in question was
used as a flag (using an equate for the value 7) – i.e. to match with a
BSET or BTST. I would use the ANDI approach if I were merely masking out
bits which I wanted zeroed. In fact, with ANDI, I might even write the
value in binary (with leading zeroes) to make it clearer which bits were
affected.
Put another way, I would choose my coding to reflect my intention. This
would help me with debugging in the future.
Regards,
Shraddhan – via Whap! from Hertfordshire in the UK
My reluctance to pad the memory to word or longword address comes in part
from the fact that I'm not sure if all printers are like my MX-80 with
GrafTrax, and willing to ignore binary zeros in character mode, and in part
because I'm not sure by the time I get all the testing for padding need
done I will still have saved that much time. Well, there are a number of
complications…
I guess I need one of those M68000 manuals with the timing diagrams. I
used to see them all the time and thought I'd buy one someday. Now it's
someday, and I never see them anymore.
As for BCLR vs ANDI, I guess even following your guidelines I can go either
way! Bit 7 is sort of a flag that means bits 0-6 are the ASCII they look
like, but with a special use. But every character in a text could be so
marked. So I'll flip a coin and find out what my own personal style is.
🙂
Bart
– via Whap!
Bart,
As for BCLR vs AND
I would say that if you are using a flag and modifying it or testing it
with BCHG, BSET and BTST then you should use BCLR, whereas if your
intention is to mask out part of some data, you should use AND.
Yes, it's really a matter of personal style. If _I_ wanted to mask out the
most significant bit of 8-bit ASCII, I would use AND.
Regards,
Shraddhan – via Whap! from Hertfordshire in the UK
Shraddhan,
At the moment I can't even remember whether I went with BCLR or AND. I
think I'll go with the philosophy that if it ain't broke, don't fix it, for
now.
I admit I have to fight back the feeling that with ANDI #7F,<ea> I'm
working with 7 bits and therefore doing seven times as much work as I would
with BCLR #7,<ea>, which just works on one; I'm sure this is purely
human psychology and the machine hardly notices the difference.
Bart
– via Whap!
Bart,
According to my book (I think – it's badly printed), both BCLR and a
byte-size AND take the same amount of time. For either instruction, the
processor fetches 8 bits of data. Then it does the work – clear one of the
8 bits or mask all 8 bits. Then it writes back the 8 bits.
The thing is, the 8 operations of masking a bit each take just as long as
clearing one bit, because the 8 are done at the same time. So you could
well say that from a philosophical point of view the processor is doing
more work for an AND, and that moreover a lot of this work is wasted;
however from a practical point of view there is nothing in it.
On a psychological level – I wonder if the processor feels p*****d off for
having to do all this extra work? Or is it just happy to have something to
do? (I read somewhere a few years back that the bulk of the computing
power in the world is spent in hanging around idly waiting for somebody to
hit a key on the keyboard. In which case, if processors were p*****d off
from having to do useless work, they would have gone on strike long ago.)
And yes, I _did_ once have a computer go on strike on me – a Commodore C64
point blank refused to carry out one particular instruction. I turned the
power off for a few minutes. Tried again. Still didn't work. Turned the
power off for a day, two days. Still didn't work. Left it for a week.
The machine relented and worked fine ever since. I swear that I'm not
making this up!
Regards,
Shraddhan – via Whap! from Hertfordshire in the UK
Shraddhan,
I knew computers could be ornery, but I didn't think you could threaten
them into working right by making them think you might never bring them
back to life again! Hmm… I wonder if that would work one of these times
when I write move.b but the computer should very well know I really mean
move.w?
Bart
– via Whap!
Your comment brings up a request I have had for YEARS. Why WHY W H Y!
won't someone make an assembler that lets you make a list file with cycle
times (as an option). Then you can look over the code and make informed
decisions. Looking it up in a book is better than nothing but hell! What
are computers for! <smile>
That's an incredible idea, it seems so obvious, that's usually the mark of
a useful concept. 🙂 Write to those assembler and compiler companies!
This would be useful for C programmers, too – turn on assembler output and
count cycles on C code! Don't forget the letters to disassembler companies,
too, like the Puzzle Factory, and debuggers like Metadigm's. And what about
some assembler keywords or C pragmas to mark the region of code you'd like
to cycle-count, with in-line totals… Obviously, the computer can't truly
cycle-count things like loops unless it knows default data values for
counter registers, etc.
Our assemblers provide cycle times just before the mnemonic field.
Unfortunately, we've only written cross assemblers to this point, 68xx
series stuff.
–Ben
Wouldn't this be something way below an assembler? Like presumably one
could write a little hack in BASIC or ARexx to find the recognized mnemonic
on each line, and output another file with the cycle time for that mnemonic
on the equivalent line along with the source line, or at least the
menmonic… how 'bout a sum also
.. the hard part is typing in a table of mnemonics and times…. If someone
sends me $10 I'll do it on a napkin at lunch.
No…
The problem is in the 68xxx series, cycle times are extremely dependant
upon what other instructions have run first, whether branches were taken,
is it in the cache, is the cache on, etc. It's hard.
–Ben
Do you have a little feature that keeps a running total, too? That sounded
like a nifty feature: with a directive, you could reset the counter, and it
would make it easier to tally the cycles within a loop or a stretch of
code, for example. Just an idea… Or maybe we can ask Larry to write an
ARexx version of this, it could simply filter any assembler source code and
add cycle counts.
John,
Yes, there are pseudo-ops that total and reset the times; others that
specify which particular CPU you have (for instance, the CMOS 68705 has a
different cycle time for many instructions than does the less expensive
NMOS part). You can also use the counter value in math expressions, and
assign it to other variables, so that a loop count can be done and then you
can pick back up with the count from the previous instructions above the
loop. It's a nice feature.
–Ben
Part of my assembler (CAPE2.5) package is a "runtime profiler" that I was
hoping might be something like what you suggest. But apparently it isn't.
I haven't figured out yet what it IS.
– via Whap!
On the processors with caches (like the 030) unrolling a loop may not
actually speed your code up…if it means your code no longer fits into the
cache, then it might even slow down.
andy
Thanks for your note about caching loops. 030s will be beyond me for some
time yet, but I do hope to make my code available to others (at least those
poor souls who picked up my AmigaBasic version).
Let's see… When I find out what page of the RKM it tells how to find out
if there's a cache or not, I can put in a branch to loop or not-loop
routines (I _think_ I'm kidding).
Bart
– via Whap!