[comp.lang.c] Looking for C functions to access PC memory

F3U@PSUVMB.BITNET (11/04/87)

I am looking for some functions when called from a C program, will:
      1)  read (peek) memory directly when supplied with
          an address.  Returns with the value at specified address.
  and
      2)  write (poke) memory directly when supplied with an
          address and a byte value.
     
I am using Microsoft C 4.0 on an AT&T 6300 PLUS.  Routines
in C or Assembler OK.
     
Thanks in advance.
     
                     Frank
                     F3U@PSUVMB.BITNET
     

kwok@iris.ucdavis.edu (Conrad Kwok) (11/07/87)

When you are using C, you don't need any special routine to read
memory. All you need to do is to declare a pointer (near or far
depending on the address) and then you may read or write to that
location using normal assignment statements. Here is an example to
read the value at location 0xC000:0x0100

	char far *ptr;

	ptr = (char far *) 0xC0000100L;
	printf("Value at 0xC000:0x0100 is %d\n", *ptr);

The above above should also works for Turbo C.

-- Conrad
internet: kwok@iris.ucdavis.edu
csnet: kwok@ucd.csnet 
csnet: kwok%iris.ucdavis.edu@csnet.relay
uucp: {seismo, uunet, lll-lcc, ...}!ucdavis!iris!kwok

platt@emory.uucp (Dan Platt) (11/08/87)

In article <24261F3U@PSUVMB> F3U@PSUVMB.BITNET writes:
>I am looking for some functions when called from a C program, will:
>      1)  read (peek) memory directly when supplied with
>          an address.  Returns with the value at specified address.
>  and
>      2)  write (poke) memory directly when supplied with an
>          address and a byte value.
>     
>I am using Microsoft C 4.0 on an AT&T 6300 PLUS.  Routines
>in C or Assembler OK.



The way I did it was to define a routine that loads a pointer with
a value pointed to by a segment/offset...
__________________________________________________________________

far char * mem_pt(seg,offst)  /* this may be coerced */
short seg,offst;
{
	union	{
		struct {
			short: ofst,sg;    /* these may be backwards */
		} vals;
		far char * ptr;
	} pointr;

	pointr.vals.sg= seg;
	pointr.vals.ofst=offst;

	return(pointr.ptr);
}

____________________________________________________________________

This was off the top of my head, and may be a little off on syntax, but
not by much.  Hope this is a help.

Dan

luis@grinch.UUCP (luis) (11/08/87)

In article <447@ucdavis.ucdavis.edu> kwok@iris.UUCP (Conrad Kwok) writes:
>When you are using C, you don't need any special routine to read
>memory. All you need to do is to declare a pointer (near or far
>depending on the address) and then you may read or write to that
>location using normal assignment statements. Here is an example to
>read the value at location 0xC000:0x0100
>
>	char far *ptr;
>
>	ptr = (char far *) 0xC0000100L;
>	printf("Value at 0xC000:0x0100 is %d\n", *ptr);
>
>[...]

Thank you for posting the example, but I have a few questions...
  1. Why is there an 'L' at the end of the address?  (does it stand for Long?)
  2. I have been trying to access the DTR on my modem, but I don't know
     what the base address is.  From the Technical Reference, I know that
     the offset is 3FE (bit 0), but can't find the base addres..  Can anyone
     help?

-------------------------------------------------------------------------------
Luis Chanu                             "Live every day as if it were your last,
UUCP: ...ihnp4!sun!aeras!grinch!luis      because one day you will be right."
UUCP: ...pyramid!wjvax!grinch!luis                              -Benny Hill
              Disk-Claimer: That's not your disk, that's my disk.
-------------------------------------------------------------------------------

ljz@fxgrp.UUCP (Lloyd Zusman) (11/08/87)

In article <2312@emory.uucp> platt@emory.UUCP (Dan Platt) writes:
>In article <24261F3U@PSUVMB> F3U@PSUVMB.BITNET writes:
>>I am looking for some functions when called from a C program, will:
>>      1)  read (peek) memory directly when supplied with
>>          an address.  Returns with the value at specified address.
>>  and
>>      2)  write (poke) memory directly when supplied with an
>>          address and a byte value.
>>     
>>I am using Microsoft C 4.0 on an AT&T 6300 PLUS.  Routines
>>in C or Assembler OK.

There already functions that do this dort of thing in Microsoft C
version 4.0 ...

void movedata(srcseg, srcoff, destseg, destoff, nbytes)
unsigned int srcseg;
unsigned int srcoff;
unsigned int destseg;
unsigned int destoff;
unsigned int nbytes;


This function copies 'nbytes' bytes of data from srcseg:srcoff to
destseg:destoff.  If you're in a small- or medium-model program,
the 'destseg' parameter can be gotten via the segread() function
(it will be the DS register value).

But if your really MUST have something like peek and poke, here's a
quickly-hacked (i.e., while I'm typing here right now) attempt at
these.  They're written to work in any memory model.  There might be
a bug or two, but this is the general idea ...

unsigned char
peek(segment, offset)
unsigned int segment;
unsigned int offset;
{
	unsigned char result;
	unsigned long address = (unsigned long)((char far *)&result);
	movedata(segment, offset, address >> 16, address & 0x0000ffffL, 1);
	return (result);
}

void
poke(segment, offset, byte)
unsigned int segment;
unsigned int offset;
unsigned char byte;
{
	unsigned long address = (unsigned long)((char far *)&byte);
	movedata(address >> 16, address & 0x0000ffffL, segment, offset, 1);
}

Shorter versions of these could be written ...

unsigned char
peek(segment, offset)
unsigned int segment;
unsigned int offset;
{
	unsigned char result;
	movedata(segment, offset, (char far *)&result, 1);
	return (result);
}

void
poke(segment, offset, byte)
unsigned int segment;
unsigned int offset;
unsigned char byte;
{
	movedata((char far *)&byte, segment, offset, 1);
}

However, these shorter versions violate strict typing conventions, as
they make use of the fact that a "char far *" takes up the same space
on the stack as two unsigned int's (on the PC).  A syntax checker like
'lint' wouldn't like this code, nor would many strict-typing purists.

I don't remember if the cast should be "char far *" or "char * far", so
if this doesn't work, try it the other way.

-- 
Lloyd Zusman, Master Byte Software, Los Gatos, California
"We take things well in hand."
...!ames!fxgrp!ljz

flaps@utcsri.UUCP (11/10/87)

In article <447@ucdavis.ucdavis.edu> kwok@iris.UUCP (Conrad Kwok) writes:
>	char far *ptr;
>
>	ptr = (char far *) 0xC0000100L;
>	printf("Value at 0xC000:0x0100 is %d\n", *ptr);

bad style.  avoid temporaries; the reader can't determine (easily) for
how long the variable is used.  restricting values to an expression, by
not storing them in variables, is modular in the same way that
restricting values to a function by storing them in local variables is.

use instead:
       printf("contents of C000:0100 is %d\n",*(char far *)0xc0000100L);

ajr

gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/10/87)

Use these sparingly!

#define	PEEK(loc)	(*(char *)(loc))
#define	POKE(loc,value)	*(char *)(loc) = (value)

To access a wide datum than a byte, change "char" to "short" or "long".

ljz@fxgrp.UUCP (Lloyd Zusman) (11/11/87)

In article <6662@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:

>#define	PEEK(loc)	(*(char *)(loc))
>#define	POKE(loc,value)	*(char *)(loc) = (value)

This would only work if you are using a large data model and the 'loc'
variable is of the type long (or some other 32-bit thing).  If you're
using Microsoft C version 4.0 (which I believe you are if I don't have
you mixed up with another contributor here), use the movedata() function,
which I described in a recent posting.  It'll work in all memory
models.


-- 
Lloyd Zusman, Master Byte Software, Los Gatos, California
"We take things well in hand."
...!ames!fxgrp!ljz

Leisner.Henr@Xerox.COM (marty) (11/11/87)

Doug,

I think your peek and poke macros will cause problems on PCs due to the
segmented nature of the beast.

With the compiler I use (Aztec) and a large data model, in order to peek
at
(say) 0xf0000 absolute, the address becomes 0xf0000000 with a double
word.

I don't think any C compiler (at least known I know off) does the
conversion from absolute addressing to segment/offset on the fly.  I
know Aztec provides several subroutines to do this.
	
marty
ARPA:	leisner.henr@xerox.com
GV:  leisner.henr
NS:  martin leisner:henr801c:xerox
UUCP: martyl@rocksvax.uucp

michael@orcisi.UUCP (11/11/87)

> 	union	{
> 		struct {
> 			short: ofst,sg;    /* these may be backwards */
> 		} vals;
> 		far char * ptr;
> 	} pointr;
> 
> 	pointr.vals.sg= seg;
> 	pointr.vals.ofst=offst;

The MSC FP_OFF and FP_SEG macros supplied in one of the standard
header files might be used here instead.  Their are valid as both
rvalues and lvalues.

rab@mimsy.UUCP (Bob Bruce) (11/12/87)

Followup-To:


In article <261@grinch.grinch.UUCP> luis@grinch.UUCP (Luis Chanu) writes:
>In article <447@ucdavis.ucdavis.edu> kwok@iris.UUCP (Conrad Kwok) writes:
>>	...
>>	ptr = (char far *) 0xC0000100L;
>
>  1. Why is there an 'L' at the end of the address?  (does it stand for Long?)

Yes, it stands for long.  Many compilers have 32 bit pointers and 16 bit
int's.  Appending an `L' to a constant is equivalent to casting it
to a long.  In an assignment, such as this, the `L' is not necessary,
but it does emphasize that the constant is bigger than an int.

>  2. I have been trying to access the DTR on my modem, but I don't know
>     what the base address is.  From the Technical Reference, I know that
>     the offset is 3FE (bit 0), but can't find the base address.  Can anyone
>     help?

Sorry, but the 3FE is a port address, not a memory location.  Check your
compiler manual.  It should provide some library routines for accessing
I/O ports.  Usually something like `outp()' and `inp()'.

If these are not provided then you are going to have to write some
assembly language routines.

moran@yale.UUCP (11/12/87)

From personal experience, when doing things like getting at ports
(i.e. doing modem control stuff), it's better to use assembly
language, and interrupts than to try to munge it out in C.
			  
			  William L. Moran Jr.
moran@{yale.arpa, cs.yale.edu, yalecs.bitnet}  ...{ihnp4!hsi,decvax}!yale!moran

"The treatment lasts one second, but the effects last your lifetime."
"Have you ever had a Pan-Galactic gargleblaster?"
"This is worse!"
"Freeowww!!"		-Hitch Hiker's Guide Radio Series

chris@mimsy.UUCP (Chris Torek) (11/12/87)

In article <151@fxgrp.UUCP> ljz@fxgrp.UUCP (Lloyd Zusman) writes:
[example `peek' and `poke' routines using `movedata' deleted]

>Shorter versions of these could be written ...

[shorter examples deleted]

>However, these shorter versions violate strict typing conventions, as
>they make use of the fact that a "char far *" takes up the same space
>on the stack as two unsigned int's (on the PC).  A syntax checker like
>'lint' wouldn't like this code, nor would many strict-typing purists.

`peek' and `poke' are already utterly machine dependent, hence it
does not matter how machine-dependently you code them.  Were I
to use an IBM PC, I might write something like this:

	#define	peek(addr)	(*(unsigned char far *)(addr))
	#define poke(addr, value) (*(char far *)(addr) = (value))

If I had to construct addresses from segment+offset, I might use
some variation on this:

	#define	buildaddr(seg, off) (((long)(seg) << 16) + (off))
	/* I thought segments were << 4 ...? */

Using `movedata' to copy one byte from one location is overkill.

Now then, as to style and machine-dependency: the routines that
actually *use* peek and poke should be hidden away inside other
routines that do whatever is really desired.  Someone mentioned
controlling DTR on an on-board modem:

	#define	DTRADDR	((char far *)0x12345678)
		/* note that the L suffix is not strictly necessary */
		/* the value above is certain to be wrong */
	#define	DTR_BIT	0x20	/* also likely wrong */

	/*
	 * Turn DTR on (if onoff!=0) or off (if onoff==0).
	 */
	set_dtr(onoff)
		int onoff;
	{

		if (onoff)
			*DTRADDR |= DTR_BIT;
		else
			*DTRADDR &= ~DTR_BIT;
	}

=> Hide machine dependent operations inside machine-independent routines. <=
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mac3n@babbage.acc.virginia.edu (Alex Colvin) (11/13/87)

> #define	PEEK(loc)	(*(char *)(loc))
> #define	POKE(loc,value)	*(char *)(loc) = (value)
> 
> To access a wide datum than a byte, change "char" to "short" or "long".

These will work in SOME program models with SOME compilers. 

In the small model, a (char *) can only point within the data segment. 
What's probably wanted here is a pointer that can reach anywhere in
memory. 

Because this issue isn't well defined by PDP-11 C, most PC compilers
have their own way of doing this.  In CII's C86 there is the movblock
routine, whose arguments are offsets and segments.  In uSoft C you can
declare (char far*) pointers, which can point outside your data segment.

Casting (loc) to a pointer is unreliable.  The meaning of a such a cast
is not well-defined.  If loc is a 16-bit int or short, this won't get
you beyond the data segment, but is probably reliable for references
(offsets) within it.  If loc is a 32-bit int or long (depends on your
compiler), it can hold a full address.  Now the question is, what's an
address?  Some compilers view such a cast as a simple trick on the type
system, in which case (loc) should consist of a segment in the most
significant 16 bits and an offset in the least significant 16 (the
actual format of a far pointer).  Other compilers try to make casts into
conversions, and assume (loc) is a 32-bit offset from the beginning of
memory, in which case (loc) has the segment number * 16 + the offset, in
32 bits. 

Best to find out what your compiler manual tells you.  Then decorate the
code with warnings and #ifdefs

			been burned before...
			mac@cs.virginia.edu

tainter@ihlpg.ATT.COM (Tainter) (11/13/87)

In article <5640@utcsri.UUCP>, flaps@utcsri.UUCP writes:
> bad style.  avoid temporaries; the reader can't determine (easily) for
[ deletions ]
> use instead:
>        printf("contents of C000:0100 is %d\n",*(char far *)0xc0000100L);
> ajr
OR, if that is too cryptic for your tastes use:
function()
{
    otherstuff;

    { /* restrict the scope of ptr */
	char far *ptr;

	ptr = (char far *) 0xC0000100L;
	printf("Value at 0xC000:0x0100 is %d\n", *ptr);
    }

    morestuff;
}

--j.a.tainter

greg@gryphon.CTS.COM (Greg Laskin) (11/14/87)

>In article <24261F3U@PSUVMB> F3U@PSUVMB.BITNET writes:
>>I am looking for some functions when called from a C program, will:
>>      1)  read (peek) memory directly when supplied with
>>          an address.  Returns with the value at specified address.
>>  and
>>      2)  write (poke) memory directly when supplied with an
>>          address and a byte value.
>>     
>>I am using Microsoft C 4.0 on an AT&T 6300 PLUS.  Routines
>>in C or Assembler OK.

In Mircrosoft C:

foo()
{
	char huge *blat =0;
	char far *p;
	char c; int i;

	/* assume n is the ABSOLUTE address of an object in physical
	   memory -- linear space, not segmented */
	long n = 0x0c0000;

	c = blat[n];
	i = ((int huge *)blat)[n];

	/* huge pointers are EXPENSIVE so if you're going to do lots of
	   stuff in a 64K segement, like video stuff, resolve the address
	   into a far pointer and use it instead thusly */
	p = &blat[0xb8000];

}

Note:

#ifndef M_LGDATA
#define huge
#define far
#endif

to remove the processor dependency of the code. (Yes.  I know about the
non-portability of the hard addressing)
-- 
Greg Laskin   
"When everybody's talking and nobody's listening, how can we decide?"
INTERNET:     Greg.Laskin@gryphon.CTS.COM
UUCP:         {hplabs!hp-sdd, sdcsvax, ihnp4}!crash!gryphon!greg
UUCP:         {philabs, scgvaxd}!cadovax!gryphon!greg

Leisner.Henr@Xerox.COM (marty) (11/14/87)

I'm kinda getting tired of the peek and poke discussion on pee-cee.

I think one of the problems with finding clean C semantics to implement
peek and poke is:  

	we're peeking and poking outside of our processes address space.


Peek and poke are not thinks which should be done unless the program has
a very good reason to do it.  If possible, find a more standard
mechanism (like a Bios interrupt which returns the requested
information).

If you run on a machine with memory protection, this discussion becomes
moot.  If you try to access memory you don't have, you get kicked out of
the system.

marty
ARPA:	leisner.henr@xerox.com
GV:  leisner.henr
NS:  martin leisner:henr801c:xerox
UUCP: martyl@rocksvax.uucp

new@udel.EDU (Darren New) (11/15/87)

I think the original poster mentioned something about reading the 
telecom status registers somewhere in the 0x300's.
Since the 8086 has the interrupt table there, and most of the
other I/O goes through I/O ports, I would assume that the
serial port is accessed through I/O instructions.
Thus, this entire discussion is probably moot anyway.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/17/87)

In article <127@babbage.acc.virginia.edu> mac3n@babbage.acc.virginia.edu (Alex Colvin) writes:
>> #define	PEEK(loc)	(*(char *)(loc))
>Some compilers view such a cast as a simple trick on the type system, ...
>Other compilers try to make casts into conversions ...

In C, a cast IS a conversion.  However, you're right in remarking that
some compilers have gotten this wrong.  It is also true that conversion
between a pointer and an integer of the proper size is implementation
specific, although I would hope that an implementation that supports
this at all would document the mapping.  Portable applications will not
resort to such tricks in the first place..