FindFirstIn


B

BartC

Kaz Kylheku said:
Yes; I believe this is a common calling convention on m68K. A pointer
return goes into A0, and an integer into D0.

Code which calls malloc without declaring it first really bites the dust.


What? That's exactly the sort of thing you exactly know for every darn
function.

I mean, instead of knowing it will always be D0, for example, you need to be
aware that it might be D0 or A0.

(I would just have made it D0, but then if you did immediately want to use
the result as an address, it needs to be copied to an A register. In
general, an extra nuisance to deal with.)
 
Ad

Advertisements

R

Richard Tobin

Kaz Kylheku said:
Yes; I believe this is a common calling convention on m68K. A pointer
return goes into A0, and an integer into D0.

No, it was an uncommon convention, precisely because it would have
broken so many C programs that didn't declare functions like malloc().
(68k-based workstations became common in the mid 80s, and needed to
support code originally written for PDP-11s and VAXes.) One or two
compilers used a0 by default but had pragmas to change that for
compatibility.

-- Richard
 
B

BartC

Keith Thompson said:
BartC said:
[MC68000]
Use the A regs for addresses, and the D regs for non-addresses. What's
the problem?

Because an address which isn't going to be used for an address is just
data;
does it go into An or Dn?

Why would you have "an address which isn't going to be used for an
address"?

In the same way that a pointer object in a C program isn't being constantly
dereferenced; mostly it us just being pushed around, copied, passed to a
function, compared etc. For those purposes, it is just data like everything
else.

And would use D registers. A registers are needed for dereferencing. Then
you have expressions such:

int** p;

(*p+i*n+j)

This dereferences p once, but then you add i*n+j (which internally also need
scaling) to it. But is the result *p in a D register or an A register? It
depends on whether the result is going to be assigned elsewhere:

int* q;
q=(*p+i*n+j);

or whether it is going to be dereferenced now:

int a;
a=*(*p+i*n+j);

or whether it's going to be returned from this function which has return
type int*:

return (*p+i*n+j);

In short, it becomes a complete nightmare trying to determine which category
of register is best to use in any situation.
So your concern is that you might have a function that returns a 32-bit
value, but you don't know whether that value is an address or not.
There are such cases, but in my experience they're rare -- and I'd
expect them to be rare even in assembly language. If a function returns
a value that can be interpreted either as an integer or as an address,
you can always return it in a D register and let the caller copy it into
an A register if necessary.

It is not a question of interpreting: you *know* a return value type is a
pointer, but you don't know if the caller is going to *dereference* the
pointer, which requires the value in an A register.
Yes, this is a small cost, but there's also a substantial benefit: the
68K has 16 registers (D0..D7 and A0..A7), but instructions only need 3
bits to select one of them; the choice between D and A is implied by the
instruction.

It just means more work by compiler writers (I'm doing some code generation
at the minute. Just the *one* set of registers is difficult enough!)
But for most functions, the meaning of the returned value should be
reasonably clear and unambiguous. A memory allocation function like
malloc() returns an address; an arithmetic function returns an integer.

If you write p=malloc(n) and a=somefn(b), the data-paths in both cases are
exactly the same. You don't need the result of malloc() to be passed in A0
rather than D0. (It would actually be rare to use the result of malloc
immediately (this would need an intervening cast):

*(malloc())=0;)
 
J

Johannes Bauer

How clever of you.

As you know, it is for windows calling conventions. For a linux version
it would be even simpler since rsi doesn't need to be saved.

Ah, I know, yes? To be honest, I didn't. I just compiled it and expected
it to work. It didn't. Then I looked at the code and saw the reason
immediately.
It is portable to:

Apparently you have no clue what "portability" means. That's like
specifying a variant for Itanium assembler and claiming that it's
"portable" to a Z80.
Mac OS X
Linux
Solaris
Windows
The Surface tablet
and a long etc.

Yes, the calling conventions change

You don't say. How clever of you.

Regards,
Johannes


--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
J

Johannes Bauer

Here is a gcc compatible version.
Tested under Mac OS X

You apparently don't even grasp the difference between a compiler and an
architecture. The version you posted is not "gcc compatible", that
doesn't even make any sense.

Maybe you should stick to writing C code.

Cheers,
Johannes

--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
J

jacob navia

Le 25/05/2014 13:42, Johannes Bauer a écrit :
You apparently don't even grasp the difference between a compiler and an
architecture.

Look Mr, I am witing for the intel architecture obviously. But I did not
specify that in every message I send because I suppose that people here
do not need to be reminded of everything at each message.


The version you posted is not "gcc compatible", that
doesn't even make any sense.

Well, it will run in Linux and Mac OS X and in all systems that follow
the gcc ABI calling convention for x86
Maybe you should stick to writing C code.

Maybe you should turn on your brain before posting.
 
Ad

Advertisements

J

jacob navia

That's an interesting idea Stephan, but I do not think that will make
any big difference because testing if a character belongs to the set
needs quite a few instructions: if((( r & t )== t )&&(( r & !b )== 0 )).

It saves some comparisons of course but addes also some overhead.

Cost/Benefits aalysis must include also the setup operations
(determining the set of bits)

But it could be that I am wrong of course, it would be a matter of
measuring woth a suitabe text+segt
 
I

Ike Naar

That's an interesting idea Stephan, but I do not think that will make
any big difference because testing if a character belongs to the set
needs quite a few instructions: if((( r & t )== t )&&(( r & !b )== 0 )).

The test can be simplified to

if ((r | t) == (r & b))
 
D

David Brown

x86 code is the closest you're going to get, without the ability to write
microcode or somehow get further inside the workings of the processor.

x86/amd64 assembly is the closest you can get - but on modern x86
processors, it's pretty far away from the real workings of the chip. On
x86 devices, the hideous x86 instructions are translated into RISC
micro-ops before execution. And while there is plenty of information
about these from Intel and AMD, the details vary wildly between
processor generations and manufacturers, and you can't program at that
level.
I disagree. Doing basic arithmetic in assembler (especially floating point)
can be educational. (But you don't want to do it more than once.)

I'd only bother with the floating point if you are particularly
interested in it. Working at the assembly level gives you an insight
into things like rounding modes, conversions between precisions, etc.
If you want to know about that stuff, then a little assembly is
educational - but most people can use floating point happily while
ignoring these details.

On the other hand, multi-length integer arithmetic in assembly can be
interesting to learn how these things work within the processor.
There're a few other things, but many can also be made available with a
more
carefully designed (than C) higher level language.

Pre-emptive multi-threaded kernels are almost always written in 95%+ C
code. There is always a little bit that has to be in assembly, but it
is usually kept to a minimum (compiler extensions are often used to
reduce the assembly parts - such extensions are, of course, slightly
outside of normal C).

I don't know what is meant by "precisely tracing" garbage collection,
but I know that garbage collection is not something that an OS should
get involved in, except in terms of recovering resources owned by a
process when it dies.
That's what I thought too when it first appeared. Until I examined it more
closely and it was even less orthogonal than x86! (For example, having
two kinds of registers, the Address and Data registers, immediately an
awkward decision needs to be made, if you're generating code, of which
to use. Some things work on D regs and not on A regs, and vice versa. Etc.)

You must have a very strange definition of "orthogonal" to think that
the m68k ISA is less orthogonal than x86. The x86 has about a dozen or
so registers, and all are highly specialised with many operations only
working on particular register combinations. Later versions of the x86
instruction set included instruction varieties and prefixes to allow
more flexibility, but it is as far from "orthogonal" as any large cpu
architecture has ever been.

The m68k, on the other hand, has two sets of main registers - data
registers D0..D7, and address registers A0..A7. To a very large extent,
instructions can choose freely amongst these. Addressing modes are
restricted mainly to using the Ax registers, and many arithmetic and
logic instructions use only the Dx registers - but each can use any of
the Ax or Dx registers (with a very few exceptions). It was far and
away the most orthogonal instruction set until RISC became popular.
 
D

David Brown

Keith Thompson said:
BartC said:
[MC68000]
Use the A regs for addresses, and the D regs for non-addresses. What's
the problem?

Because an address which isn't going to be used for an address is just
data;
does it go into An or Dn?

Why would you have "an address which isn't going to be used for an
address"?

In the same way that a pointer object in a C program isn't being constantly
dereferenced; mostly it us just being pushed around, copied, passed to a
function, compared etc. For those purposes, it is just data like everything
else.

And would use D registers. A registers are needed for dereferencing. Then
you have expressions such:

int** p;

(*p+i*n+j)

This dereferences p once, but then you add i*n+j (which internally also
need
scaling) to it. But is the result *p in a D register or an A register? It
depends on whether the result is going to be assigned elsewhere:

p is a pointer, as is *p. So you load *p into an A register. i and j
are data, so you load them into D registers (unless they are
compile-time constants of small enough size to use directly). The
arithmetic is done in D registers, or as part of a more complex
addressing mode, and you use (Ax + Dy) or (Ax + Dy + j) addressing modes
to access the final data.

In fact, the 68020 onwards had an addressing mode that would often do
all this in one instruction. If **p is in Ax, i is in Dy (or Ay), and n
is 1, 2, 4 or 8, and j is a compile-time constant, then it can access
*(*(p + offset) + i*n + j) in a single addressing mode.

No problems, no mixups about address and data.
int* q;
q=(*p+i*n+j);

or whether it is going to be dereferenced now:

int a;
a=*(*p+i*n+j);

or whether it's going to be returned from this function which has return
type int*:

return (*p+i*n+j);

In short, it becomes a complete nightmare trying to determine which
category
of register is best to use in any situation.

By no stretch of the imagination is this "a complete nightmare". I
think if you need to apply serious thought as to whether something
should go in an A register or a D register, you have probably screwed up
the logic of your program (if it ever had any).
It is not a question of interpreting: you *know* a return value type is a
pointer, but you don't know if the caller is going to *dereference* the
pointer, which requires the value in an A register.

If they are not going to dereference the pointer, it can still happily
go in an A register! You can do plenty of operations on the A registers
other than dereferencing - you can load them and store them, and do some
basic arithmetic such as adding offsets. The operations that you
/cannot/ do directly on an A register are things like multiplication and
logic instructions, which don't make sense on addresses.
It just means more work by compiler writers (I'm doing some code generation
at the minute. Just the *one* set of registers is difficult enough!)

What cpu are you targeting? The x86 doesn't have a "set of registers" -
it has a bunch of individualists. Even the amd64 instruction set is a
mess, though it is far cleaner than the original x86. Other compiler
writers considered the m68k ISA to be a dream come true when they
started working with it.
 
K

Kenny McCormack

Le 25/05/2014 13:42, Johannes Bauer a écrit :

Look Mr, I am witing for the intel architecture obviously. But I did not
specify that in every message I send because I suppose that people here
do not need to be reminded of everything at each message.

I don't understand why you keep bothering with these morons.
Well, it will run in Linux and Mac OS X and in all systems that follow
the gcc ABI calling convention for x86

I don't understand why you keep bothering with these morons.
Maybe you should turn on your brain before posting.

I don't understand why you keep bothering with these morons.

Really, Jacob, you are better than all this tripe.

As is freely admitted here, none of these jerks has ever written a compiler
(with, I think, 1 [maybe 2] exception(s) - and, when you get right down to it,
those 1 [or 2] are not jerks like Mr. Bauer is). You have. This puts you
so far out of their league that them trying to jerk you around is like an
ant tugging on an elephant.

In fact, most of them are PROUD of the fact that they've never written a
compiler, because it fits in with their general "We know nothing about
internals or mechanics. Everything we need to know about anything is
written in the Bible (Oops, I mean 'C standard')" mentality. You can see
this in a lot of Kiki's (and others) writing; pride in ignorance is a
pretty common Republican/Christian trait.

As many other Bible-thumpers are fond of saying, "The Bible is inerrant; it
says so right here in the Bible."

BTW, most of what I've just written applies as well to Malcolm. He actually
discusses concepts and ideas here (Where does he get off doing that???) and
all he gets in return is BS nit-picking.

That's the world of CLC...

--
"I heard somebody say, 'Where's Nelson Mandela?' Well,
Mandela's dead. Because Saddam killed all the Mandelas."

George W. Bush, on the former South African president who
is still very much alive, Sept. 20, 2007
 
Ad

Advertisements

B

Ben Bacarisse

David Brown said:
The m68k, on the other hand, has two sets of main registers - data
registers D0..D7, and address registers A0..A7. To a very large
extent, instructions can choose freely amongst these. Addressing
modes are restricted mainly to using the Ax registers, and many
arithmetic and logic instructions use only the Dx registers - but each
can use any of the Ax or Dx registers (with a very few exceptions).
It was far and away the most orthogonal instruction set until RISC
became popular.

Them's fightin' words. Data General's Nova? (But maybe I'm forgetting
all the odd bits).

<snip>
 
B

BartC

David Brown said:
On 24/05/14 11:21, BartC wrote:
You must have a very strange definition of "orthogonal" to think that the
m68k ISA is less orthogonal than x86. The x86 has about a dozen or so
registers, and all are highly specialised with many operations only
working on particular register combinations.
The m68k, on the other hand, has two sets of main registers - data
registers D0..D7, and address registers A0..A7. To a very large extent,
instructions can choose freely amongst these. Addressing modes are
restricted mainly to using the Ax registers, and many arithmetic and logic
instructions use only the Dx registers - but each can use any of the Ax or
Dx registers (with a very few exceptions). It was far and away the most
orthogonal instruction set until RISC became popular.

Look at the eight core arithmetic operations of the 8086 (ADD, ADC, SUB,
SBB, AND, OR, XOR, CMP).

These work with any of the 8 registers (disregarding that the top 4 byte
registers don't map to the top 4 word registers), using combinations such as
reg/mem or mem/reg.

Now look at the same eight on the 68K: largely they will only work on the
data registers while two of them only seem to work in one direction: EOR and
CMP. (For CMP it probably doesn't matter, yet the 8086 did allow a
comparison either way around).

But then you also have sorts of special, ad-hoc instructions to make some of
these work with address registers, or between data and address registers,
and many of the above can't be used at all.

It *is* a nuisance having two incompatible banks of registers; if they'd had
an extra couple of bits to be able to specify two lots of 16 registers
in an instruction field, instead of two lots of 8, would they still have
done it that way? No!

(But it's some 30 years since I first looked at the 68K and arrived at my
conclusion. And since I couldn't persuade my boss to make use of this
processor (probably too expensive), it didn't really matter.)
 
B

BartC

David Brown said:
p is a pointer, as is *p. So you load *p into an A register. i and j are
data, so you load them into D registers (unless they are compile-time
constants of small enough size to use directly). The arithmetic is done
in D registers, or as part of a more complex addressing mode, and you use
(Ax + Dy) or (Ax + Dy + j) addressing modes to access the final data.

No, you load p into an A register, in other to fetch the value of *p. But is
that result itself also put into an A register, or a D register?

Assuming you are going to use this pointer value you are constructing, the
*p value is loaded into an A register too. And assuming the hardware can't
directly take care of i*n+j, this arithmetic now needs to be performed. But
while you can add into an A register, you can't multiply using them, so that
part is done in D registers.

But if you are only interested in constructing a pointer value to be used
later (ie. treating it as data), then *p can be loaded into a D register.

Wouldn't it be have been much simpler though if there wasn't the distinction
between A and D types of register?
By no stretch of the imagination is this "a complete nightmare". I think
if you need to apply serious thought as to whether something should go in
an A register or a D register, you have probably screwed up the logic of
your program (if it ever had any).

Suppose you have an 64-bit 'double' value, and need to compare it with
another. If you're using x64, would it be loaded into R0 (rax), or into the
FPU?

If that requires a bit of consideration, does that mean the 'program' is
screwed up?
If they are not going to dereference the pointer, it can still happily go
in an A register! You can do plenty of operations on the A registers
other than dereferencing - you can load them and store them, and do some
basic arithmetic such as adding offsets. The operations that you /cannot/
do directly on an A register are things like multiplication and logic
instructions, which don't make sense on addresses.

Logic instructions can make sense, if for example your data is 4-byte
aligned and you want to make use of those two lower bits of a pointer which
are otherwise
zero.

But my point of view is for generating automatic code (for a compiler for
example). Having to constantly think about A or D or either is a
distraction.
What cpu are you targeting? The x86 doesn't have a "set of registers" -
it has a bunch of individualists.

The x86-32 model I'm targeting has 6 general 32-bit registers, R0 to R5
(there is also Rframe and Rstack), as I call them. Plus the FPU (I'm not
bothering with XMMn and all that for the time being.)

There are a few instructions which use dedicate registers (DIV and SHL for
example), but otherwise they are reasonably orthogonal.
 
S

Stefan Ram

Ike Naar said:
The negation '!' should probably be a bitwise negation '~'.

Yes, thank you!, that was my intention: to mask all bits that
are not set in any of the characters from the source set and
see that those are all 0.
 
Ad

Advertisements

K

Kaz Kylheku

x86 code is the closest you're going to get, without the ability to write
microcode or somehow get further inside the workings of the processor.

Machine code does not reveal how a processor works. I coded in 6502
assembly for years and gained no clue about how instructions are fetched,
decoded and executed: how the CPU is internally organized.

Not until I studied digital design, and designed a very simple processor at the
gate level.
I disagree. Doing basic arithmetic in assembler (especially floating point)
can be educational. (But you don't want to do it more than once.)

I should have said "in comparison to doing it in C", but I thought it
was clear.

Coding "algorithmic stuff" in assembly language not having to do with detailed
control over the state of the machine, or taking advantage of precise
instruction-level exceptions and the like, does not really teach you anything,
other than perseverence and patience.

In various machine languages, there are various "bit twiddy things" that
are not directly available in C, like byte swapping loads in a single
instruction, or bitfield extracting/injecting instructions and the like, but
that's not particularly educational. You can write these in C as functions.
 
S

Stefan Ram

jacob navia said:
Cost/Benefits aalysis must include also the setup operations
(determining the set of bits)

Sometimes, when the set of characters to be searched for is
the same for multiple searches, it might be possible that
the setup operations have to be done only once for several
texts to be searched later.

In the case of the test

(( r & t )== t )&&(( r & ~b )== 0 )

, sometimes, only the LHS of the && is actually evaluated.
 
I

Ian Collins

David said:
The m68k, on the other hand, has two sets of main registers - data
registers D0..D7, and address registers A0..A7. To a very large extent,
instructions can choose freely amongst these. Addressing modes are
restricted mainly to using the Ax registers, and many arithmetic and
logic instructions use only the Dx registers - but each can use any of
the Ax or Dx registers (with a very few exceptions). It was far and
away the most orthogonal instruction set until RISC became popular.

As one who moved from PDP11 to 68K, I'd say the PDP11 held that crown.
When I moved to 68K, I thought of it as a natural extension to PDP11.
My first encounter with the 8086 architecture was one of those WTF
moments :)
 
Ad

Advertisements

K

Keith Thompson

BartC said:
Wouldn't it be have been much simpler though if there wasn't the
distinction between A and D types of register?
[...]

Perhaps. But the cost would have been either to have half as many
registers, or to dedicate 4 rather than 3 bits for each register
reference in an instruction.

Flexibility is not always free.

And this isn't really about C, is it?
 

Similar threads

M
Replies
12
Views
604
BartC
B
D
Replies
92
Views
5K
BartC
B
J
Replies
35
Views
891
Stephen Sprunk
S
A
Replies
4
Views
655
Ben Bacarisse
B
P
2
Replies
25
Views
963
Ike Naar
I
R
Replies
11
Views
589
Keith Thompson
K
A
Replies
16
Views
2K
Barry Schwarz
B
S
Replies
57
Views
2K
Tim Rentsch
T
Top