Programming in C - Some doubts

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
M-374 LX
Very interested
Posts: 61
Joined: Mon Aug 11, 2008 10:15 pm
Contact:

Programming in C - Some doubts

Post by M-374 LX » Mon Aug 11, 2008 10:25 pm

In the initialization of graphics, the code is similar to this:

Code: Select all

register uint *pw;

pw = (uint *) GFXCNTL; /*P1*/

*pw = 0x8016;   /*P2*/
*pw = 0x8174;   
*pw = 0x8230;   
*pw = 0x832C;   /*P3*/
In P1, it is pointing to the control port of VDP, it is right.

From P2 to P3, it is setting the VDP.
Why the value changes, instead of just using bitwise operators (&, |, ^, ~)?

ElBarto
Very interested
Posts: 160
Joined: Wed Dec 13, 2006 10:29 am
Contact:

Post by ElBarto » Mon Aug 11, 2008 11:36 pm

I don't really understand your question.
But doing *pointer = XXXX is a way to write some data at the address in pointer.
So in this code, you set pw to the address of hte VDP CTRL address then write some value at this address.

M-374 LX
Very interested
Posts: 61
Joined: Mon Aug 11, 2008 10:15 pm
Contact:

Post by M-374 LX » Mon Aug 11, 2008 11:41 pm

Why the value of *pw changes?
Why is not enough just one value?

I saw other detail, that looks clearer:

Code: Select all

    pw = (uint *) GFXDATA;
    *pw = 0;        /* color 0 - black */
    *pw = 0x0eee;   /* color 1 - white */
Why are the while and black colors being set in the same place?

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Tue Aug 12, 2008 2:10 am

I'm no C expert (in fact I don't understand it almost at all), the PW in this code acts as I/O port, and each "=" would mean a write to that port.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

ElBarto
Very interested
Posts: 160
Joined: Wed Dec 13, 2006 10:29 am
Contact:

Post by ElBarto » Tue Aug 12, 2008 2:33 am

pw is a pointer to a memory zone.
In the code, you set the value of pw (the memory address) just once :

Code: Select all

pw = (uint *) GFXDATA;
Then by doing :

Code: Select all

*pw = 0;        /* color 0 - black */ 
*pw = 0x0eee;   /* color 1 - white */
You didn't change the value of pw, but change the value at the address of pw.
Read some book about C and pointers you'll better understand this.[/code]

Jorge Nuno
Very interested
Posts: 374
Joined: Mon Jun 11, 2007 3:09 am
Location: Azeitão, PT

Post by Jorge Nuno » Tue Aug 12, 2008 2:34 am

It may seem that the first write is useless and he is complaining about it:

*pw = 0;
*pw = 0xeee;

But thats how the VDP works, you always write to the same location (Vctrl or Vdata), but internally the VDP auto-increments the destination address, even if the pointer is unchanged...

M-374 LX
Very interested
Posts: 61
Joined: Mon Aug 11, 2008 10:15 pm
Contact:

Post by M-374 LX » Tue Aug 12, 2008 2:42 am

ElBarto wrote: You didn't change the value of pw, but change the value at the address of pw.
Yes, I wanted to say this.

Let me see if I understood:
The first value that I write to the address of pw will keep at the first position from pw;
The second value that I write to the address of pw will keep at the second position from pw;
The third [...] at the third position;
And successively.

Is all this right?

furrykef
Interested
Posts: 30
Joined: Mon Jul 21, 2008 7:28 pm

Post by furrykef » Tue Aug 12, 2008 3:08 am

M-374 LX wrote:Why the value changes, instead of just using bitwise operators (&, |, ^, ~)?
You don't seem to quite understand how memory-mapped I/O (often abbreviated MMIO) works... you're still thinking in terms of RAM. MMIO doesn't work like RAM, so forget what you know about it. In particular, GFXCNTL doesn't represent a "value" at all. It's actually a port that happens to be written to and read from in the same way as memory.

What you're really doing when you write to GFXCTNL is issuing commands, not writing data. The multiple writes simply represent multiple commands You really need some docs on the VDP (that's what we call the graphics chip) to fully understand it, and even then it may take you a while to understand everything that's going on, but here's the basic idea.

The VDP has 23 registers which are used to control various aspects of the display. When a 16-bit value is written to GFXCNTL, this value is a command to set one of the registers. The high byte specifies the register number, which is 0x00 to 0x17, plus the highest bit is always set, so it reads as 0x80 to 0x97. The low byte is the value that is inserted into the register; what the value means depends on which register you're using. (Note that these registers are write-only. You can read from GFXCNTL, but doing so just gives you a bunch of flags.)

Thus, in the line "*pw = 0x8016;", it's really saying "Set VDP register 0 to 0x16." Here's where you consult your docs to find out what a value of 0x16 in VDP register 0 means.

GFXDATA is sort of similar, but in this case you actually are writing data rather than commands. First, before writing to GFXDATA you must write a 32-bit value to GFXCTNL. Writing a 32-bit value works completely differently from writing a 16-bit value, and it's a bit complicated, but it basically specifies where in VRAM you want to write to or read from. (Consult the docs for The writing and reading is done through GFXDATA, but it can only be set to read or write, not both, depending on the value you sent to GFXCNTL.

The VDP contains an internal "pointer" to VRAM (or CRAM, or VSRAM, but I'll call 'em all "VRAM" for convenience). Every time you write a value to GFXDATA, this pointer is incremented. How much it's incremented depends on the value in VDP register 15 (hex 0f). So by repeated writes to GFXDATA, you will write to the VRAM in a linear fashion. I think the most typical way to do things is to have register 15 set to 2 and to read or write using 16-bit and 32-bit values. So if you do this, supposing you told the VDP to write at 0000, the first word you write will be at 0000, the second will be at 0002, the third at 0004, etc.

Here's where things get odd, and I might have this completely wrong (I've done very little Genesis dev so far), so be careful, but I think GFXDATA works like this. GFXDATA accepts 8-bit writes and 16-bit writes. If you do a 32-bit write, it will interpret it as two 16-bit writes. This affects how the VDP's internal pointer gets updated. If register 15 is set to 2, then writing a byte will increase it by 2, and writing a word will also increase it by 2, but writing a dword will increase it by 4 (because it's interpreted as two words, hence it increments twice).

As for docs, google for genvdp.txt for some pretty good docs on the VDP. (Note that C00004 is GFXCNTL and C00000 is GFXDATA.) There's also sega2f.doc, which contains much of the same info (though less in the way of explanations) as well as a bunch of other stuff. Also be sure to refer to genesis.c and genesis.h for some helpful #defines and routines, as well as to help you figure out what's going on.

- Kef

TMorita
Interested
Posts: 17
Joined: Thu May 29, 2008 8:07 am

Post by TMorita » Thu Aug 14, 2008 7:51 pm

Don't forget to declare the pointer as pointing to a volatile value, otherwise the compiler is allowed to remove all the writes except the last one.

Toshi

ElBarto
Very interested
Posts: 160
Joined: Wed Dec 13, 2006 10:29 am
Contact:

Post by ElBarto » Thu Aug 14, 2008 11:47 pm

TMorita wrote:Don't forget to declare the pointer as pointing to a volatile value, otherwise the compiler is allowed to remove all the writes except the last one.

Toshi
Only if you compiled your binary with -O

furrykef
Interested
Posts: 30
Joined: Mon Jul 21, 2008 7:28 pm

Post by furrykef » Thu Aug 14, 2008 11:52 pm

ElBarto wrote:Only if you compiled your binary with -O
You'd have to be a fool not to use at least -O1, I think, unless maybe you're debugging. In any case, it's very stupid not to use the "volatile" because its inclusion will not hurt anything and its omission definitely can hurt something.

Not to mention that if your code follows the ANSI standard rather than being specific to a compiler like GCC, then flags become irrelevant anyway; you must use "volatile" there.

- Kef

Post Reply