[comp.lang.c] Dereferencing Typecast Integer Literals

avery@netcom.UUCP (Avery Colter) (02/04/91)

I would like to know something.

In BASIC (waiting for the groans to subside), there were the POKE, PEEK,
and CALL commands, which respectively store a value in a location, read
the present value of a location, or start executing a machine language
function starting at the location.

Now, obviously, in C, the most obvious way to do any of these is to declare
a char pointer. Dereferencing this pointer as an r-value PEEKs, dereferencing
it as an l-value POKEs, and dereferencing it with a parameter list postfixed
CALLs.

However..... since all pointer objects are pseudo-integer in nature, and since
they are considered to be typecast cousins of the true integers, my question
is whether it is necessary to even declare a pointer for these tasks.

I can't find any explicit reerence to it in either of the books I have,
so what I'd like to know is:

1. Since a constant literal integer of the appropriate size for the
implementation can be typecast into a pointer to any type, is it then 
possible to dereference the result of this typecast expression?

i.e.

{
char s;
...
s = *(char *)0xC000; /* C000: address of the keyboard strobe on Apple II */
...
}

2. If #1 can be done, then can a dereferenced typecast integer constant
be used as an l-value?

i.e.

*(char *)0xC010 = 0; /* C010: value zeroed to signal ready to take another
			  character into C000 			    */

3. Can a DTIC have a parameter list postfixed to make it into a function call?

i.e.

*(void (*()))0xFC58();  /* FC58: address of firmware HOME function */

What's the consensus on all this? Is it better to just declare const
pointer objects for all the firmware points you wish to have on hand?


-- 
Avery Ray Colter    {apple|claris}!netcom!avery  {decwrl|mips|sgi}!btr!elfcat
(415) 839-4567   "I feel love has got to come on and I want it:
                  Something big and lovely!"         - The B-52s, "Channel Z"

krey@i30fs1.ira.uka.de (Andreas Krey) (02/04/91)

In article <22700@netcom.UUCP>, avery@netcom.UUCP (Avery Colter) writes:
> I would like to know something.
> 
> In BASIC (waiting for the groans to subside), there were the POKE, PEEK,
> and CALL commands, which respectively store a value in a location, read
> the present value of a location, or start executing a machine language
> function starting at the location.
> 

In C you may even define macros to make it more readable,
but it IS system dependent...
All macros cast the integer address to an appropriate pointer type
and then dereference it to get the lvalue/function.

#define LOC(x) (*((unsigned char*)(x)))
#define CALL(x) (((void (*)())(x))())

*(void (*()))0xFC58()

'BASIC'   <=>  'C'
a=PEEK(b) <=>  a=LOC(b);
POKE(b,c) <=>  LOC(b)=c;
CALL(x)   <=>  CALL(x);

If you want Parameters for Call:

#define FUNC(x) ((void (*)())(x))

CALL(f,a,b) <=> FUNC(x)(a,b);

(No Warranty for BASIC Syntax.)

Andy

-------------------------------------------
.signature: No such file or directory
.signature: Still no such file or directory

gwyn@smoke.brl.mil (Doug Gwyn) (02/05/91)

In article <22700@netcom.UUCP> avery@netcom.UUCP (Avery Colter) writes:
>Now, obviously, in C, the most obvious way to do any of these is to declare
>a char pointer. Dereferencing this pointer as an r-value PEEKs, dereferencing
>it as an l-value POKEs, and dereferencing it with a parameter list postfixed
>CALLs.

In some implementations the latter is not supported, and the specific
integral type needed to hold a data "address" for such usage is
implementation dependent.  (So be careful!)

>However..... since all pointer objects are pseudo-integer in nature, and since
>they are considered to be typecast cousins of the true integers, my question
>is whether it is necessary to even declare a pointer for these tasks.

I don't understand that at all.  In C, each object consists of one or
more bytes, and within (also "one past") an object a linear address
space model is assumed.  Also, each implementation is required to
provide SOME integral type to which any data pointer can be cast such
that upon subsequent cast back to the same type of pointer, the new
pointer value will compare equal to the old value.  This may actually
involve some weird run-time implementation of "pointer equivalence
classes", depending on what the machine architecture is like.

>1. Since a constant literal integer of the appropriate size for the
>implementation can be typecast into a pointer to any type, is it then 
>possible to dereference the result of this typecast expression?

The major premise is false.  It is NOT required that an arbitrary
integral value be convertable to an object pointer, only that some
suitable representation as an integer (of the implementation-dependent
type) exist for each object pointer that is valid in the higher-level
C abstract-machine model.  In particular, there may be a range of
"addresses" that are completely inaccessible from a C program; this is
usually the case for virtual-memory operating systems and others that
have separate physical address spaces for user versus supervisor tasks.
Also, even if the memory is accessible from C, you have to obey object
alignment constraints; for example, full-word accesses may require that
the integer representation be a number that is exactly divisible by 2
or by 4.

>s = *(char *)0xC000; /* C000: address of the keyboard strobe on Apple II */

On most Apple II C compilers, this would indeed pick up the data at that
"soft switch" location.  You need to be aware that in some cases (although
not likely in this case) the compiler can generate multiple accesses to
the location even though you have specified it only once in the source
code.  For I/O registers, including Apple II soft switches, there are
often side effects from an access, such as clearing a flip-flop, and many
locations behave differently for read versus write access.  ANSI C has
introduced the "volatile" type qualifier to provide a handle for the C
programmer to try to obtain as near to a one-to-one mapping between the
explicit source code and the generated machine instructions as possible;
in older C implementations one simply had to be aware of what code would
be generated for certain C constructs and then make sure that the proper
constructs were used to obtain the desired effect.  It is still highly
recommended that you inspect the generated code to make sure that you are
getting the desired effect.

>2. If #1 can be done, then can a dereferenced typecast integer constant
>be used as an l-value?
>*(char *)0xC010 = 0; /* C010: value zeroed to signal ready to take another
>			  character into C000 			    */

Sure, why not?  Once the integer has been converted to a pointer, you are
back in the domain of standard C.

>3. Can a DTIC have a parameter list postfixed to make it into a function
>call?

There is no guarantee that this will work, although some implementations
will support it.

>*(void (*()))0xFC58();  /* FC58: address of firmware HOME function */

This, however, is not syntactically correct.  You should instead try
	((void (*)())0xFC58)();
or if your compiler is brain-damaged
	(*(void (*)())0xFC58)();

>What's the consensus on all this? Is it better to just declare const
>pointer objects for all the firmware points you wish to have on hand?

Recommended practice is to define macros for such accesses, using yet
other macros that implement generic access of a certain type.  E.g.

	#define PEEK(addr,type) (*((type)*)(addr))
	#define	KBDSTROBE PEEK(0xC000,char)

datangua@watmath.waterloo.edu (David Tanguay) (02/05/91)

In article <15067@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>Also, each implementation is required to
>provide SOME integral type to which any data pointer can be cast such
>that upon subsequent cast back to the same type of pointer, the new
>pointer value will compare equal to the old value.

Where does the standard say this?  3.3.4 says a pointer may be converted
to some integral type, and that an integer may be converted to a pointer,
but the result is implementation defined. Whether or not the pointer
value is preserved across pointer -> int -> pointer is implementation
defined. For example, the pointer -> integer conversion could drop the
segment of the pointer, and the integer -> pointer could apply some
default segment (the integer gets the offset).
-- 
David Tanguay            Software Development Group, University of Waterloo

hp@vmars.tuwien.ac.at (Peter Holzer) (02/10/91)

gwyn@smoke.brl.mil (Doug Gwyn) writes:

>On most Apple II C compilers, this would indeed pick up the data at that
>"soft switch" location.  You need to be aware that in some cases (although
>not likely in this case) the compiler can generate multiple accesses to
>the location even though you have specified it only once in the source
>code.  For I/O registers, including Apple II soft switches, there are
>often side effects from an access, such as clearing a flip-flop, and many
>locations behave differently for read versus write access.  ANSI C has
>introduced the "volatile" type qualifier to provide a handle for the C
>programmer to try to obtain as near to a one-to-one mapping between the
>explicit source code and the generated machine instructions as possible;
>in older C implementations one simply had to be aware of what code would
>be generated for certain C constructs and then make sure that the proper
>constructs were used to obtain the desired effect.  It is still highly
>recommended that you inspect the generated code to make sure that you are
>getting the desired effect.

That reminds me of a deficiency of C for low-level programming, a friend
of mine recently discovered. Volatile tells the compiler that a memory
location might change its value independantly of the program, const
tells the compiler that it may not write to a location, but how do you
tell the compiler that a certain location may not be read? 

In our case the code was something like:

* some_port = value;
shadow = value;

which the compiler "optimized" to:
*some_port = value;
shadow = * some_port;
which caused a bus error :-(

We solved the problem by declaring value volatile, which is extremely
non-obvious and misleading, but does its job.

--
|    _  | Peter J. Holzer                       | Think of it   |
| |_|_) | Technical University Vienna           | as evolution  |
| | |   | Dept. for Real-Time Systems           | in action!    |
| __/   | hp@vmars.tuwien.ac.at                 |     Tony Rand |

gwyn@smoke.brl.mil (Doug Gwyn) (02/11/91)

In article <2309@tuvie.UUCP> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>We solved the problem by declaring value volatile, which is extremely
>non-obvious and misleading, but does its job.

In fact that's a good use for "volatile".  It forces the compiler to
try to use the variable in the generated code in a one-to-one
correspondence with the explicit accesses in the C source code.