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.