It is currently Mon Dec 18, 2017 4:09 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Oct 16, 2012 1:42 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
I was playing around with cc65, and I noticed that something like the following:
Code:
((unsigned char*)0)[0x2007] = a

Compiles into to something equivalent to:
Code:
LDX #$20
STX $11
LDX #$00
STX $10
LDY #$07
STA ($10), Y


This example is simplified, it isn't exactly what you get from cc65, but it seems cc65's [] operator in this case results in an indexed indirect store similar to this one.

What I discovered, though, is that across all the emulators I tried, STA to $2007 via an indirect indexed address like this appears to increment the PPU write address by two, rather than just one. On the first increment, PPU memory is not written. On the second increment, my value in A is stored to the PPU. So... it skips the byte I was aiming for and writes the next one instead! What is it about indirect indexed addressing that causes this behaviour?

Anyhow, this is also a warning, I guess, that if you're going to use cc65, don't try to write memory mapped registers this way. (edit: see posts below for syntax that does not have this problem.)


Last edited by rainwarrior on Tue Oct 16, 2012 9:18 am, edited 3 times in total.

Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 1:48 am 
Offline

Joined: Sat Jan 23, 2010 11:41 pm
Posts: 1161
I don't really understand, why ((unsigned char*)0)[0x2007] = a rather than *((unsigned char*)0x2007)=a ?


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 1:53 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Ah! That works much better, thanks!
Code:
*((unsigned char*)0x2007) = a

Generates:
Code:
STA $2007


And by the way, the reason I had done it the other way first was because of suggestion #12 in this cc65 doc: http://www.cc65.org/doc/coding.html
I misapplied it... I suppose the advice is only for when wanting to add to a pointer as an index, not for using static addresses like this.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 7:09 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19355
Location: NE Indiana, USA (NTSC)
Which then leads to header files looking like this:
Code:
#define PPUCTRL   (*(volatile unsigned char*)0x2000)
#define PPUMASK   (*(volatile unsigned char*)0x2001)
#define PPUSTATUS (*(volatile unsigned char*)0x2002)
#define OAMADDR   (*(volatile unsigned char*)0x2003)
#define OAM_DMA   (*(volatile unsigned char*)0x4014)
#define PPUSCROLL (*(volatile unsigned char*)0x2005)
#define PPUADDR   (*(volatile unsigned char*)0x2006)
#define PPUDATA   (*(volatile unsigned char*)0x2007)


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 7:47 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
tepples wrote:
Which then leads to header files looking like this:
Code:
#define PPUCTRL   (*(volatile unsigned char*)0x2000)
#define PPUMASK   (*(volatile unsigned char*)0x2001)
#define PPUSTATUS (*(volatile unsigned char*)0x2002)
#define OAMADDR   (*(volatile unsigned char*)0x2003)
#define OAM_DMA   (*(volatile unsigned char*)0x4014)
#define PPUSCROLL (*(volatile unsigned char*)0x2005)
#define PPUADDR   (*(volatile unsigned char*)0x2006)
#define PPUDATA   (*(volatile unsigned char*)0x2007)

I like to do this:
Code:
struct _PPU {
    byte ctrl;
    byte mask;
    byte const status;
    byte oam_addr;
    byte oam_data;
    byte scroll;
    byte addr;
    byte data;
};
#define    PPU         ( *( struct _PPU volatile * )0x2000 )

(BTW, volatile has no effect in CC65 currently.)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:19 am 
Offline

Joined: Sat Sep 15, 2012 6:58 pm
Posts: 103
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:23 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
exdeath wrote:
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.

Most perfect? No way, it should/could know that the address is constant and optimize accordingly.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:27 am 
Offline

Joined: Sat Sep 15, 2012 6:58 pm
Posts: 103
thefox wrote:
exdeath wrote:
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.

Most perfect? No way, it should/could know that the address is constant and optimize accordingly.


:mrgreen:


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:28 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Heh, mine is now just:
Code:
#define RAW_BUS(x) (*(unsigned char*)(x))

Am I the only one who likes to use the registers by number instead of naming them?

With cc65's weak optimizer, volatile isn't really capable of doing anything, but the sentiment is right, semantically.

How does the compiler like that struct, TheFox? Does it manage to reduce 0x2000 + offset at compile time? (Edit: apparently it does! Turns into the STA $2007 it deserves.)

Also, nobody has any insight as to what is special about STA (zp), Y? That was the question I was most interested in. Why does it generate an extra increment?


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:37 am 
Offline

Joined: Sat Sep 15, 2012 6:58 pm
Posts: 103
rainwarrior wrote:
Also, nobody has any insight as to what is special about STA (zp), Y? That was the question I was most interested in. Why does it generate an extra increment?


Yeah... not sure. Whats the 6502 bus doing on that opcode? Maybe the ZP accesses for the base address cause dummy bus accesses that confuse the PPU and toggle a false write and increment?


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:46 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3969
STA (xx),Y adds a dummy read.
Code:
        1      PC       R  fetch opcode, increment PC
        2      PC       R  fetch pointer address, increment PC
        3    pointer    R  fetch effective address low
        4   pointer+1   R  fetch effective address high,
                           add Y to low byte of effective address
        5   address+Y*  R  read from effective address,
                           fix high byte of effective address
        6   address+Y   W  write to effective address

They did it this way in case they needed to fix up the high byte before performing a write, because they figured that reads wouldn't have side effects like writes would.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Last edited by Dwedit on Tue Oct 16, 2012 8:51 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 8:51 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
exdeath wrote:
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.

One of the primary advantages of C over assembly is that the compiler is able to pick from equivalent implementations of a statement, so that it can do "what's best" for the situation. There really isn't such a thing as "exactly what you tell it". That's an assembly programming concept, not a C concept.

Frankly I'm a little disturbed that cc65 isn't able to tell the difference between a static pointer and a variable in this case. It's really weird too, because if I create a named static array (via assembly/linker), it manages to reduce just fine into an absolute address. It's this strange case where ((unsigned char*)x) isn't treated as a static pointer with the [] operator. Not intuitive at all.

For instance if I do something like this:
Code:
.segment "PPU_REGISTERS"
_ppu_register: .res 8
.export _ppu_register

I can get well behaved results from something like:
Code:
extern unsigned char ppu_register[8];
ppu_register[7] = a;


It's only when using a number literal cast to an address that it has problems with []. As TheFox pointed out, you can cast it to a struct and it has no problem at all!


Last edited by rainwarrior on Tue Oct 16, 2012 9:02 am, edited 2 times in total.

Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 9:00 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Thanks Dwedit. Is that copied from a reference somewhere? (I'd like to read it, if it exists.)


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 9:02 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3969
It's from this file:
http://nesdev.com/6502_cpu.txt

Yeah, there's a bunch of files linked from the main page of the site. But this one looks like the best for knowing what the CPU is actually doing.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Tue Oct 16, 2012 9:17 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
exdeath wrote:
thefox wrote:
exdeath wrote:
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.

Most perfect? No way, it should/could know that the address is constant and optimize accordingly.


:mrgreen:

Wat.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Yahoo [Bot] and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group