It is currently Sat Nov 18, 2017 6:08 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 89 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6
Author Message
PostPosted: Sat Nov 14, 2015 2:49 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
tokumaru wrote:
(I don't even have a GitHub account)

I just tried to sign up and found out that someone had already done it using my email address somehow, and I'm pretty sure it wasn't me. The account appears to have never been used, so I stole it for me and created a new issue.

Anyway, if this macro behavior is by design (which would be pretty weird), at least it would be good to have confirmation on this.


Top
 Profile  
 
PostPosted: Sat Nov 14, 2015 3:19 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
BTW, I have another dilemma in my hands right now, regarding subroutines. This isn't as much of a headache as the previous problems, but I'd still like your input on this.

.proc is cool and all, but it has a serious problem that prevent me from using it for some of my subroutines. Since using .proc is equivalent to defining a label and immediately starting a scope with the same name, this means that all the code for the subroutine must be AFTER its entry point. However, sometimes I optimize loops by branching to labels BEFORE the subroutine's entry point, like in my "Wait16Cycles" subroutine (BTW, the 6 cycles from the jsr count as part of the first 16 cycles):

Code:
WasteTime: ;<- This is not an entry point!

   ;waste time (11 cycles)
   pha
   pla
   nop
   nop

Wait16Cycles:

   ;decrement the counter (2 cycles)
   dex

   ;decide between wasting more time (3 cycles) or returning (2 cycles)
   bne WasteTime

   ;return (6 cycles)
   rts

In some cases you can design the subroutine differently and branch to after the rts, or find another way to have the entry point at the very top, but in some cases, you really do need the entry point in the middle, or multiple entry points.

What I'm currently doing is this:

Code:
   .scope Wait16Cycles

WasteTime:

   ;waste time (11 cycles)
   pha
   pla
   nop
   nop

Entry:

   ;decrement the counter (2 cycles)
   dex

   ;decide between wasting more time (3 cycles) or returning (2 cycles)
   bne WasteTime

   ;return (6 cycles)
   rts

   .endscope

   Wait16Cycles = Wait16Cycles::Entry

Which is not exactly pretty, but works. I also have to create global aliases in the end for any additional entry points and for scratchpad variables defined inside the subroutine's scope if code on the outside ever uses them for passing arguments to the subroutine.

What do you guys think about this?


Top
 Profile  
 
PostPosted: Sat Nov 14, 2015 4:14 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5822
Location: Canada
Eh, I generally only ever used scope to solve name conflict issues that already existed when trying to modify stuff I'd already written. For new code I don't tend to need scope at all, and only occasionally want to use proc, though I make heavy use of local labels (@/:).

Like, it's a useful feature when I need it, but I don't go out of my way to put everything in its own scope, etc. If proc isn't really appropriate for the way you use your subroutines, why use it at all for those ones? You're going out of your way to put a simple inside a scope, and then doing more work to take it out of that scope at the end.

Hmm, actually I'm wondering, do you know about local labels? They're basically the reason I don't really find much need for scope/proc.


Top
 Profile  
 
PostPosted: Sat Nov 14, 2015 5:07 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
rainwarrior wrote:
do you know about local labels?

I do, and I make heavy use of them, but they don't solve everything. Unmanned labels make the code unreadable and harder to maintain when used for larger branches/jumps, so I only use them for very localized stuff. Cheap local labels are good, but when you have multiple (global) entry points, access to cheap local labels becomes limited within the subroutine. To solve these cases I'd still need aliases, so instead of going all Frankenstein and coding each subroutine using a different methodology, I though it was better to go with the solution that works for all cases and be consistent. I find it easier to program if I decide on a single style because I start coding in that style without giving much thought to it, while using different styles depending on the situation would require more analysis each time.

Scopes seemed like a good way to isolate the subroutines, except that since you can't forward-reference scopes, You need these kinds of aliases to make the subroutine and the parts of it that need to be accessed from the outside visible.


Top
 Profile  
 
PostPosted: Sat Nov 14, 2015 7:14 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
tokumaru wrote:
Which is not exactly pretty, but works. I also have to create global aliases in the end for any additional entry points and for scratchpad variables defined inside the subroutine's scope if code on the outside ever uses them for passing arguments to the subroutine.

Depending on your exact use case, you may be able to use something like this for ".proc with an entry point in the middle":
Code:
.scope foo
    nop
    nop

::foo:
    nop
    nop
    rts
.endscope
bar := foo ; Just showing that "foo" is available in global scope now.

There's no way to reference the parent scope though (only global scope), so this won't work the same as .proc with nested scopes.

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


Top
 Profile  
 
PostPosted: Sat Nov 14, 2015 7:26 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
Oh, I don't know why I didn't think of that, considering that I have created labels like that in other situations. Thanks for the idea.


Top
 Profile  
 
PostPosted: Thu Nov 26, 2015 5:53 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
Just found another very bizarre bug: https://github.com/cc65/cc65/issues/235

I was using symbols to keep track of what macros were doing, but everything broke when I started using those macros inside scopes... Turns out that expressions don't look for symbols in outer scopes. In this particular case, it seems I can make the symbols global and access them with ::, but that definitely sounds like a bug to me.


Top
 Profile  
 
PostPosted: Thu Nov 26, 2015 7:18 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19226
Location: NE Indiana, USA (NTSC)
It doesn't know that you aren't going to redefine (and shadow) Symbol later in the same scope. Perhaps a better rule would be the one fo-fo suggested in a reply: use in a constant expression forbids shadowing of the same variable name later in any enclosing scope.


Top
 Profile  
 
PostPosted: Thu Nov 26, 2015 7:40 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
tepples wrote:
It doesn't know that you aren't going to redefine (and shadow) Symbol later in the same scope.

Yeah, I figured that much. It looks pretty inconsistent though, when you reference it just fine in a instruction and in the next line it fails as part of an expression. I understand WHY it happens, but it's weird.

Quote:
Perhaps a better rule would be the one fo-fo suggested in a reply: use in a constant expression forbids shadowing of the same variable name later in any enclosing scope.

Yeah, that would probably be the best way to handle this.


Top
 Profile  
 
PostPosted: Thu Nov 26, 2015 8:01 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19226
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
tepples wrote:
It doesn't know that you aren't going to redefine (and shadow) Symbol later in the same scope.

Yeah, I figured that much. It looks pretty inconsistent though, when you reference it just fine in a instruction

Expressions can be evaluated later, even up to link time, so long as the expression is not in a context that theoretically has a way of producing a variable number of bytes. This means no conditional assembly (.if argument), no variable repetition (.repeat argument), and no variable length binary to decimal conversion (.sprintf argument).


Top
 Profile  
 
PostPosted: Fri Nov 27, 2015 1:32 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
Quick update on my variable declaration issues: I completely stopped mucking around with overlapping segments, dummy segments, and all that crap, because I though my macros were weird as fuck, and my config file wasn't exactly pretty either. I could barely understand them after a while. Turns out I could do everything I wanted simply by marking the start of the segments with labels, calculating all offsets with numeric variables, and creating variables by adding the offsets to the marker labels. Now I have a better understanding of what can and can't be done in a single-pass assembler, so I was able to plan things better.


Top
 Profile  
 
PostPosted: Sat Nov 28, 2015 9:08 am 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
tokumaru wrote:
I was using symbols to keep track of what macros were doing...it seems I can make the symbols global and access them with ::, but that definitely sounds like a bug to me.


Not sure if this was mentioned exactly: You can create scopes that are for your macro and related macros, like:

Code:
.scope mysupermacrovars
    foo .set 0
    bar .set 1
.endscope

.macro mysupermacro

  .if mysupermacrovars::foo
       ; do this
   .endif

.endmacro


Anything that is not explicitly scoped will be inside the same scope as where the macro was called/expanded from.


Top
 Profile  
 
PostPosted: Mon Nov 30, 2015 6:11 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
Yeah, I got the hang of how scopes work now. My main problem was figuring out the details of the kind of structure I wanted my program to adhere to, and in the process I kept thinking about all the things I would ever want to do, borrowing ideas from high-level programming languages and object-oriented programming. I love all the freedom you have when coding in ASM, but if you don't create a set of rules to make everything consistent, you can get lost pretty easily when working on bigger projects.

Sometimes a specific solution looks so clean and concise, almost perfect, but then you think of that one case where something will go wrong if you do it that way, so you gotta go back to the drawing board. I'm all set now though, and I'm pretty comfortable with ca65 now after having played a bit with it and understanding what it can and can't do.


Top
 Profile  
 
PostPosted: Fri Nov 10, 2017 12:21 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10112
Location: Rio de Janeiro - Brazil
It's been a couple years since I started this thread, and since I'm much more familiar with ca65 now than I was back then, I decided to revisit some of the ideas presented here.

The one thing I have in mind right now is how to automatically align the end portion of a PRG-ROM bank to the end of the bank, so that interrupt vectors, reset stub, trampoline routines, etc. are placed at the same position in every bank (simulating a fixed bank on mappers that don't have one) without wasting any space or needing manual tweaking of addresses or padding.

So far I've been using rainwarrior's suggestion of using two segments and padding the first one according to how much I've used of both, pushing the second one up. The main problem with this is that the amount of used space can only be calculated based on the relative distance between two labels, so the whole bank must be assembled as a single module.

Then there was this post from thefox, which I didn't understand at the time, but now I realize I can use a similar trick to have the linker automatically pad the lower part of the bank:

Code:
MEMORY {
   # this outputs the upper part and calculates its size
   FIXED: start = $0000, size = $8000, file = "fixed.bin", fill = no, define = yes;
   # this provides the correct PC for the upper part
   DUMMY: start = $10000 - __FIXED_LAST__, size = $8000, file = "", fill = no;
   # this outputs the lower portion and pads it as necessary
   BANK: start = $8000, size = $8000 - __FIXED_LAST__, file = "bank.bin", fill = yes;
}
Code:
SEGMENTS {
   BANK: load = BANK, run = BANK, type = ro;
   FIXED: load = FIXED, run = DUMMY, type = ro;
}

This works fine, the only catch is that since the fixed area has to be output first so we know its size, we have to output the two parts to separate files and concatenate them later (using copy /b or whatever) to put everything in the correct order. Of course I would rather have the assembler output the final ROM directly, instead of dealing with 32 temporary files for 16 PRG-ROM banks, so I decided to run this by you guys to see if there's anything I'm missing, or if this is indeed as close as it gets to "automatic right justifying".

I still don't understand why we can't use the symbols created by define = yes in the SEGMENTS section... That would definitely make things easier.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 89 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6

All times are UTC - 7 hours


Who is online

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