It is currently Sun Dec 17, 2017 12:53 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Fri Jul 20, 2012 3:39 pm 
Offline
User avatar

Joined: Sun Nov 09, 2008 9:18 pm
Posts: 1013
Location: Pennsylvania, USA
In the CA65 wiki, there's an article on using symbols in local scopes. In some cases, using local scope makes the assembler assume absolute mode when you really wanted zp. If you assemble the following nonsense program (no flags required just ca65 the_below_code.asm), you'll get a range error because the sta b0 instructions all assemble to absolute mode:

Code:
.globalzp b0

.proc test
 
  lda b0
 
.proc nested_proc

  beq label
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  sta b0
  .repeat 86
  nop
  .endrepeat
 
label:

  rts

.endproc
   
.endproc


To correct this, it appears you can do a couple of things. One, suggested by the wiki, is to add the global scope specifier in front of the use of your symbol:

Code:
.globalzp b0

.proc test
 
  lda ::b0

....


What I find odd is, I only have to do this once (such as the first time the symbol is used). Another way to correct the issue is to remove that nested .proc and just use a normal label.

What's further odd is, I remember using nested .procs for scope in the Nomolos codebase. Yet, I haven't yet found a place where CA65 is assembling to absolute mode where I'm expecting zeropage instructions.

Anyone understand this quirk of CA65 scope better than I do? Posting this will probably just make nesasm and asm6 adherants happy that they never made the switch :)


Last edited by GradualGames on Sun Aug 06, 2017 5:43 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 20, 2012 4:31 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3969
Sounds like a bug to me. I'm surprised that it would have those kind of problems.

ASM6 is aggressive about using zeropage mode whenever possible. It also uses a 3-pass system just in case it can't decide whether or not to use zeropage mode at the appropriate times. Deciding whether or not to use zeropage mode instructions is usually obvious, but sometimes it isn't. Especially once you support external symbols.

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


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 21, 2012 2:19 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
The reason this happens is that CA65 doesn't know whether "b0" is referring to the global symbol, or a symbol of the same name in the current scope which could be defined later. This has been discussed several times in the CC65 mailing list: http://www.cc65.org/mailarchive/2011-07/9539.html

I agree it's kind of unfortunate that it behaves like this, especially because most of the time it will not give a warning when it ends up using absolute addressing for zeropage variables. This is actually the reason that I added warning messages to NintendulatorDX whenever a game uses absolute addressing when zeropage could be used.

A case could be made that it would make more sense for CA65 to just use the global symbol, and if a symbol with the same name is defined later in the scope, just error out, because the case that CA65 is trying to protect against (having a global and local symbol with the same name) is so uncommon.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 12:37 pm 
Offline
User avatar

Joined: Sun Nov 09, 2008 9:18 pm
Posts: 1013
Location: Pennsylvania, USA
This old thread is making me wish asm6 and ca65 would have a baby together. It'd be the best assembler ever.

It looks like there are some funny edge cases ca65 has from being a one pass assembler, that a multi pass assembler can (apparently) easily resolve. Amusing that the author states they made ca65 a one pass assembler because it would be "more fun." Haha. Yeah maybe, but also more annoying to use perhaps :lol:


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 1:36 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Just to clarify, because this thread looks slightly scarier until I checked this for myself, but the problem is restricted to nested scopes. A top level .proc or .scope does not suffer from this problem, so far as I can tell.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 1:48 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
When I first got started with ca65 I was very excited and made heavy use of scopes in attempts to encapsulate stuff and make my code more organized. I soon ran into all kinds of annoying little issues that made me revert to using only one level of scoping.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 1:53 pm 
Offline
User avatar

Joined: Sun Nov 09, 2008 9:18 pm
Posts: 1013
Location: Pennsylvania, USA
tokumaru wrote:
When I first got started with ca65 I was very excited and made heavy use of scopes in attempts to encapsulate stuff and make my code more organized. I soon ran into all kinds of annoying little issues that made me revert to using only one level of scoping.


Yeah I think I'm headed that direction. I'm currently using .scope ... .endscope rather liberally just for local labels, but I think that's probably been overkill when I can just use cheap locals or :.

To be honest though I think I'm mainly suffering from OCD today, wishing I could have *absolute* (heh) control over whether the assembler is emitting zp or absolute in all cases. With the workarounds I've come up with for the few instances where this is happening, I'm seeing very little performance gain. And not that huge of a savings in code size either.

Despite its flaws, I can't imagine working without ca65's benefits, still, particularly the clunky but very useful macro system.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 2:12 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
rainwarrior wrote:
Just to clarify, because this thread looks slightly scarier until I checked this for myself, but the problem is restricted to nested scopes. A top level .proc or .scope does not suffer from this problem, so far as I can tell.

Hmm, might be that I'm too out of touch with ca65 (haven't coded much 6502 in a long time), but that seems really strange.

I tried a few things:
Code:
.globalzp foo
.proc bar
    lda foo ; 2 bytes
.endproc

Code:
.globalzp foo
.proc bar
    lda foo ; 2 bytes
    .proc baz
        lda foo ; 3 bytes
    .endproc
.endproc

Code:
.globalzp foo
.proc bar
    .proc baz
        lda foo ; 2 bytes
    .endproc
.endproc

I can kind of understand the logic for why it sometimes pessimistically produces absolute addressing (due to the reasoning in my previous post in this thread), but why would the same logic not affect top-level scopes?

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


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 2:19 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
GradualGames wrote:
To be honest though I think I'm mainly suffering from OCD today, wishing I could have *absolute* (heh) control over whether the assembler is emitting zp or absolute in all cases.

You can:
Code:
   lda z:var ; generates ZP instruction, generates an error if var is >= $100
   lda a:var ; generates ABS instruction


And a really weird kicker to this: if you use z: in a nested scope it will manage to generate the ZP instruction.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 2:23 pm 
Offline
User avatar

Joined: Sun Nov 09, 2008 9:18 pm
Posts: 1013
Location: Pennsylvania, USA
rainwarrior wrote:
GradualGames wrote:
To be honest though I think I'm mainly suffering from OCD today, wishing I could have *absolute* (heh) control over whether the assembler is emitting zp or absolute in all cases.

You can:
Code:
   lda z:var ; generates ZP instruction, generates an error if var is >= $100
   lda a:var ; generates ABS instruction


And a really weird kicker to this: if you use z: in a nested scope it will manage to generate the ZP instruction.


I was experimenting with the a: and z: prefixes today, in combination with the .addrsize function. However I get "address size unknown" warnings. And, with my macro system I might be passing in zp vars or ram vars, it's not guaranteed to be zp.

One thing I'm considering is simply prefixing or suffixing ALL zp symbols throughout my entire game with z_ and detecting this with the string macro technique in this thread, to determine whether to use z: or a:. That just feels like overkill though to be honest. I think I can live with imperfect assembly given how little performance or code size improvement I seem to be getting with workarounds in place anyhow. The productivity benefits from the macro system I've come up with have been utterly huge (for me, no idea if they seem like a hacky mess to others lol).


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 2:28 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Ah, I think the "rule" it's applying is that it will only search for existing symbols one level higher.

Code:
   var0 = $33
   .proc first
      lda var0 ; assumes ZP
      var1 = $55
      .proc second
         lda var0 ; assumes ABS
         lda var1 ; assumes ZP
      .endproc
   .endproc


So, that at least makes it a consistent behaviour, but still unintuitive. Trying to think what I'd want... a recursive search instead of a one-level search would seem OK to me, I don't see how that's logistically much different (i.e. still "importing" stuff from higher scopes). It already gives a range error if you redefine a symbol locally with a larger size, so that's not really a problem either.

Can anyone think of any new problems a recursive search would cause over the existing one-level search?


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 3:01 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
GradualGames wrote:
One thing I'm considering is simply prefixing or suffixing ALL zp symbols throughout my entire game with z_ and detecting this with the string macro technique in this thread, to determine whether to use z: or a:.

I don't really understand how typing z_ every time you use a variable is going to be more convenient than typing z: every time you use it? (Especially if you're going to encumber it with complicated macros...)

GradualGames wrote:
That just feels like overkill though to be honest. I think I can live with imperfect assembly given how little performance or code size improvement I seem to be getting with workarounds in place anyhow.

Well, I'd tend to think of the uses without z: or a: prefix as an "assembler's choice" option, i.e. I don't really care, and might want to move variables around later anyway. That's generally my default position, because most ZP use isn't mission critical.

In general, though, I end up looking directly at the generated code often enough when debugging, so I'm usually checking up on the assembler enough to know where it's succeeding or failing at stuff like this. That's enough to inform me of various do's and don't for keeping the output like I expect it to be.

...and when I'm looking at some really performance critical code, I look at the output closely. Focused attention where it's needed.


I never knew about this nested scopes problem because I've only ever used .scope or .proc but never wanted to put a .proc inside a .scope. TBH I think the benefits of .proc are somewhat marginal anyway, and .scope I mostly used when I was dealing with .include of lots of files into one big assemble, but normally these days I encapsulate things by assembling indvidual files instead.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 3:13 pm 
Offline
User avatar

Joined: Sun Nov 09, 2008 9:18 pm
Posts: 1013
Location: Pennsylvania, USA
rainwarrior wrote:
GradualGames wrote:
One thing I'm considering is simply prefixing or suffixing ALL zp symbols throughout my entire game with z_ and detecting this with the string macro technique in this thread, to determine whether to use z: or a:.

I don't really understand how typing z_ every time you use a variable is going to be more convenient than typing z: every time you use it? (Especially if you're going to encumber it with complicated macros...)
[


I attempted to pass in z: to my macros but this resulted in errors...not sure why yet. If I can resolve those then my proposed hack with z_ would be unnecessary.


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 3:23 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
rainwarrior wrote:
Ah, I think the "rule" it's applying is that it will only search for existing symbols one level higher.

I tried to debug this a little bit, and I think that may not be the case.

It seems like the reference to var0 in the scope first creates a symbol with flag SF_REFERENCED (in that scope), however that symbol doesn't have an address size associated to it. So it finds the symbol (reference), but later notices it doesn't have an address size and so uses the default (abs).

As a quick hack I tried to make it ignore SF_REFERENCED symbols (in addition to SF_UNUSED that's there already), and it seems to do the "right thing", but I'm not familiar enough with the code to know whether this change might have some adverse effects.

The relevant code:
https://github.com/cc65/cc65/blob/maste ... xpr.c#L580 This calls SymFindAny
https://github.com/cc65/cc65/blob/maste ... tab.c#L428 SymFindAny does search recursively.

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


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 3:34 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19353
Location: NE Indiana, USA (NTSC)
GradualGames wrote:
I was experimenting with the a: and z: prefixes today, in combination with the .addrsize function. However I get "address size unknown" warnings. And, with my macro system I might be passing in zp vars or ram vars, it's not guaranteed to be zp.

In your macros, try .assert arg < $0100, error, "address must be direct page" to ensure that the address is suitable and then use <arg to short-circuit range checking.


EDIT: Removed a misguided reply to rainwarrior based on reading his comment too fast


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 5 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