It is currently Tue Nov 21, 2017 7:08 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 89 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next
Author Message
PostPosted: Mon Nov 02, 2015 12:03 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10116
Location: Rio de Janeiro - Brazil
Oh, you're right! This is really starting to make sense to me! :D


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 12:24 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
Oops, did a little test, and I think I overstated the ability of .res, ha ha. Again, .res needs to be constant at assemble time, but it also needs to be constant in the first pass, it seems, which puts a few more requirements on us. The whole bank needs to be in one assembly unit, this way (not necessarily in the same file, but all files must be combined into one unit with .include). Everything for the ROM except the fixed code must go in one big segment. In this example, the segment is named "BANK".

Code:
.segment "BANK"
bank_start: ; this must be the first line in the BANK segment so that its total size can be calculated

; other code can go here

; the FIXED code can go anywhere, as long as appears before the padding at the end of BANK which appears below
; the critical thing is that FIXED_SIZE must be known before we get to the padding, so that we can calculate a size for padding
.segment "FIXED"
fixed_start:
; fixed code here
.assert * = $FFFA, error, "Vectors misaligned"
.word vec0, vec1, vec2
FIXED_SIZE = * - fixed_start

; finally the padding at the end of BANK segment
; this must appear after FIXED_SIZE is defined
.segment "BANK"
BANK_SIZE = * - bank_start
.res $10000 - (FIXED_SIZE + BANK_SIZE) ; generate padding


Trying to do the alignment via padding rather than the linker ends up requiring you to use a single SEGMENT and a single assembly for the whole bank, but maybe that's okay for you because that's how other assemblers do things anyway.


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 2:53 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
Two things to keep in mind about ca65:

- It doesn't know where the code is going to be placed (unless you explicitly state it with .org), but it needs to know the size of the data/code that it has generated. So stuff like .res $1234-* won't work in relocatable ("non-org") mode.
- It's a single-pass assembler, so if you try to reference labels that haven't been defined yet, it may or may not work, depending on the case:

Code:
BlockStart:
nop
nop
BlockEnd:
; Fine, the value is known at this point.
.res BlockEnd-BlockStart

Code:
; Error, need to know value (affects amount of generated data)
.res BlockEnd-BlockStart
BlockStart:
nop
nop
BlockEnd:

Code:
; Fine, value not known but doesn't affect amount of generated data, so
; actual calculation can be deferred.
.byte BlockEnd-BlockStart
BlockStart:
nop
nop
BlockEnd:

Sometimes it would be nice if it could resolve expressions like that (in cases that are resolvable, i.e. no recursive definitions and so on), but usually it's not a deal breaker.

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


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 6:36 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10116
Location: Rio de Janeiro - Brazil
rainwarrior wrote:
Again, .res needs to be constant at assemble time, but it also needs to be constant in the first pass, it seems, which puts a few more requirements on us.

Thanks for verifying this. Honestly though, this isn't such a big issue, so if the whole thing gets too awkward I might just manually define the size of the fixed block and adjust later, to make proper use of the assembler's capabilities. The main reason I decided to try CA65 is that I want to use segments so I can group relevant pieces of code regardless of where in the ROM they'll end up. In ASM6 I have to always mind the order in which code is written and/or files are included, so everything ends up where it should, and this is getting annoying to maintain because each sub-system has parts scattered everywhere.

thefox wrote:
It's a single-pass assembler

Will keep that in mind, thanks.

Anyway, here's another question: How can I keep track of the indices of the different program banks? For example, if I placed a subroutine in a segment called "BANK13", how can I know which bank to switch to in order to call that subroutine without having to manually type the number 13 to switch banks? The goal is to not have to track down references to that bank in case I decide to move the routine to a different bank later on. In ASM6, where I do everything linearly, I just have a symbol incremented every time a new bank starts, and I copy the value from that symbol to another symbol I can use for switching banks (e.g. AUDIO_BANK = CURRENT-BANK).


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 7:04 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
tokumaru wrote:
Anyway, here's another question: How can I keep track of the indices of the different program banks? For example, if I placed a subroutine in a segment called "BANK13", how can I know which bank to switch to in order to call that subroutine without having to manually type the number 13 to switch banks? The goal is to not have to track down references to that bank in case I decide to move the routine to a different bank later on. In ASM6, where I do everything linearly, I just have a symbol incremented every time a new bank starts, and I copy the value from that symbol to another symbol I can use for switching banks (e.g. AUDIO_BANK = CURRENT-BANK).

There's an attribute "bank" that can be specified for memory areas in the linker configuration. It's an arbitrary 32-bit number that can be retrieved from symbols with .bank in source code.

E.g. if you have a memory area "PRG13" that specifies "bank=13", and you have a segment "BANK13" that you place in "PRG13", then:
Code:
.segment "BANK13"
.proc foo
  rts
.endproc

; ...
; somewhere else:

lda #.lobyte( .bank( foo ) ) ; loads 13 (have to use .lobyte since it's a 32-bit number)
jsr mapBank
jsr foo

This works across file boundaries (the correct bank is substituted at link time).

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


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 12:08 pm 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19237
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
Quote:
FIXED: load = PRG, type = ro, start = $FF90;

Again, you have to manually adjust the start depending on how much fixed stuff you have? There's no way of saying "align this arbitrarily sized block to the end of the bank"? Maybe it's possible in a hackish way, but that's actually one of the things I'm trying to avoid.

Correct. I've never seen a linker that allows "right justifying" code, or specifying the location based on the address of the end of a segment rather than based on the start of a segment. Do you want me to go request this feature in ld65 on the cc65 project's issue tracker on GitHub?

Quote:
Would it be acceptable for me to use hundreds of .export statements in a bank that contains patterns for hundreds of animation frames so I can create pointers to that data in the main engine in order to access it during vblank?

Yes. I did something like this for level data in a recent project.

Quote:
That's the exact kind of thing I want to avoid... I want my code to be flexible, so I don't have to edit several lines of code every time I resize a variable or rename something.

Then I guess I've elicited another possible ld65 feature request from you: "union segments". Consider RAM segments A and D that are usable at all times and RAM segments B and C are not usable simultaneously. You want to lay out BSS segments B and C at the end of BSS segment A, wherever that might end up, and BSS segment D that starts at the maximum of the end of segments B and C, wherever that might end up.
Code:
,---------------.
|               |
|       A       |
|               |
+-------+-------+
|       |       |
|   B   |       |
|       |   C   |
`-------+       |
        |       |
,-------+-------+
|               |
|       D       |
|               |
`---------------'


rainwarrior wrote:
These are additional constraints you're dropping on me now after the fact.

tokumaDRW?


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 5:56 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10116
Location: Rio de Janeiro - Brazil
thefox wrote:
There's an attribute "bank" that can be specified for memory areas in the linker configuration.

Ah, that's perfect! That's the kind of thing I'm looking for, built-in functionalities to do things that required convoluted hacks in other assemblers.

tepples wrote:
Do you want me to go request this feature in ld65 on the cc65 project's issue tracker on GitHub?

If you feel like this might be useful to a reasonable number of users, then by all means, go ahead. I personally wouldn't feel comfortable making feature requests at this point, since I'm not really acquainted with the software yet.

Quote:
Quote:
Would it be acceptable for me to use hundreds of .export statements

Yes. I did something like this for level data in a recent project.

Good to know that this isn't such an alien thing to do, but I'd still try to avoid it, if possible.

Quote:
"union segments"

Sounds interesting.

Quote:
tokumaDRW?

Oh man, I'll try harder to not come off like that. I definitely didn't make up things after the fact though, since all of this stuff is already implemented and working in ASM6. I might have failed to provide all the details though, so sorry about that! :wink:


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 7:17 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
tokumaru wrote:
Would it be acceptable for me to use hundreds of .export statements

There's really no problem with using hundreds of import/export/global directives, if you want to. It's not like they're "expensive", it's just another symbol in a table somewhere. The linker just looks 'em up and sticks the data where it needs to go.

From a code design standpoint, there's probably something to be said for interfaces that are well contained, so that everything that belongs together is in the same translation unit, and your cross-unit communication (import/export) is minimal. However, you've got a real program to write, and if lots of linked symbols seems like it would help, there's no pressing reason to avoid it.

tepples wrote:
Do you want me to go request this feature in ld65 on the cc65 project's issue tracker on GitHub?

The strongest use case I can think of for "align to end" is DPCM samples. It's hard to let them resize to fit your data automatically, but also make room for stuff pushing up from below. As an alternative to "align to end", a "start at or above" feature would be just as good to meet its requirements, I think.

tepples wrote:
"union segments"

I can't really think of a compelling reason a feature like this would be needed. Yes, it would apply to tokumaru's specific request, well, but that needs that request represents can be satisfied in other ways, so I can't think of a more practical reason to want this feature. Actually, even "align to end" would be a good solution in this case, i.e. if you align all your temporary pages to end, you no longer need everything in the same translation unit for that ".res" to represent the shared space. You could just protect the bottom edge of your temporary space with a link-time .assert, and both regions could just grow toward the middle safely.

tokumaru wrote:
tepples wrote:
tokumaDRW?

Oh man, I'll try harder to not come off like that.

I certainly wasn't trying to make that comparison. I was merely explaining that if you had given me the additional constraints up front, I could have easily given you a solution that met them.


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 8:12 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
As long as we're talking about new features and what the linker can't do currently, when I was toying around with FME-7 I noticed this one case that the linker can't really handle: Let's say you have an 8 KB bank, and you want to map it at two different addresses in the memory space at (possibly) different times. So, for example, you have some code that should go at $8000, and another unrelated piece of code (or DMC sample) that should go at $C200, and you want to place those in the same bank. Since you can specify only one base address for the memory area, this can't be done.

Not sure what kind of a new feature would be the best way to model that type of a thing. Possibly another set of definitions for banks within the output file, and multiple memory areas could output data into the same bank (and the actual base address of a memory area would depend on how much stuff is in the bank by the time the memory area gets added there).

Probably not a big deal in practice, unless you're really short of space, but still kind of annoying that it's not at all possible, AFAIK (without .org).

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


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 8:52 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
thefox wrote:
Since you can specify only one base address for the memory area, this can't be done.

Segments have a "load" attribute (where it goes in MEMORY block) and "run" attribute (where it is expected to be run from, i.e. controls PC). By default "run" inherits "load", but otherwise you can use this to run a segment in a different MEMORY block than it gets stored in.

The only limitation I can think of is that you'd really need to start the segment at a specific address, so that you can line it up on both sides. Normally this feature is intended for run-from-RAM code, so the intention is that the linker will give you some defined symbols telling you where to place the code in RAM. With ROM, obviously that's not flexible anymore, so you'd have to specify it in the segment itself, I think.


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 9:29 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
rainwarrior wrote:
thefox wrote:
Since you can specify only one base address for the memory area, this can't be done.

Segments have a "load" attribute (where it goes in MEMORY block) and "run" attribute (where it is expected to be run from, i.e. controls PC). By default "run" inherits "load", but otherwise you can use this to run a segment in a different MEMORY block than it gets stored in.

Thanks for the comment. I actually think it helped me figure out a good solution for this:

Code:
# linker.cfg
MEMORY {
    # The actual bank that goes to the file.
    PRG0:
        start=$0000, # Address doesn't matter since it won't be used.
        size=$2000,
        fill=yes,
        file=%O;

    # A dummy memory area for data going to $8000, not written to file.
    PRG0_8000:
        start=$8000,
        size=$2000,
        define=yes,
        file="";

    # Another dummy area for data going to $C000, not written to file.
    # Start address and size depends on how much data got placed in PRG0_8000.
    PRG0_C000:
        start=$C000+(__PRG0_8000_LAST__-__PRG0_8000_START__),
        size=$2000-(__PRG0_8000_LAST__-__PRG0_8000_START__),
        file="";
}

SEGMENTS {
    CODE0_8000: load=PRG0, run=PRG0_8000, type=ro;
    CODE0_C000: load=PRG0, run=PRG0_C000, type=ro;
}


Code:
.segment "CODE0_8000"
.proc at8000
    jmp *
.endproc

.segment "CODE0_C000"
.proc atC000
    jmp *
.endproc


Result:
Code:
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  4C 00 80 4C 03 C0 00 00 00 00 00 00 00 00 00 00  L.€L.À..........
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
...

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


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 9:41 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
Interesting, that looks very useful!

I did not know about the LAST symbol, or that it could be considered a constant you could use for subsequent MEMORY blocks. I'd always run into issues trying to use generated values like that, but I'd always tried to do it through SEGMENTS; I guess the linker goes through the MEMORY blocks one at a time, in order, with each block's symbols generated before trying to resolve the next one?


Top
 Profile  
 
PostPosted: Mon Nov 02, 2015 9:47 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
rainwarrior wrote:
Interesting, that looks very useful!

I did not know about the LAST symbol, or that it could be considered a constant you could use for subsequent MEMORY blocks. I'd always run into issues trying to use generated values like that, but I'd always tried to do it through SEGMENTS; I guess the linker goes through the MEMORY blocks one at a time, in order, with each block's symbols generated before trying to resolve the next one?

Yeah I was not sure at first whether I would be able to use it there either. I'm glad it works. I'm not sure if there's an actual good reason why it doesn't work in segment definitions. There's a chance it's a bug.

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


Top
 Profile  
 
PostPosted: Tue Nov 03, 2015 6:50 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10116
Location: Rio de Janeiro - Brazil
How about scratchpad variables? In ASM6, I reserve 32 bytes in ZP that subroutines can use however they want. Then, for each subroutine, I use an .enum to define variables in that area (to keep in the spirit of not typing addresses manually, which is error-prone and annoying to maintain):

Code:
   .enum Scratchpad
   RowPointer .dsb 2
   BlockCount .dsb 1
   .ende

Any way to achieve similar behavior in ca65, without having to create hundreds of overlapping segments, one for each subroutine?


Top
 Profile  
 
PostPosted: Tue Nov 03, 2015 6:55 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19237
Location: NE Indiana, USA (NTSC)
thefox wrote:
I noticed this one case that the linker can't really handle: Let's say you have an 8 KB bank, and you want to map it at two different addresses in the memory space at (possibly) different times. So, for example, you have some code that should go at $8000, and another unrelated piece of code (or DMC sample) that should go at $C200, and you want to place those in the same bank. Since you can specify only one base address for the memory area, this can't be done.

If you don't want to use multiple MEMORY areas or run addresses for segments in a bank, possibly because they're [tokumaru]not automatic[/tokumaru], I've thought of a third way. You can leave it at $8000, and then in your sample address table entry macro, map anything in $8000-$BFFF to $C000-$DFFF.

The reuse of the __*_LAST__ symbol looks interesting as well. If it works, it'd resolve one of the feature requests above. Is there a counterpart to GREATEST() or MAX() function in ld65 config language?
  • Union segments part I (multiple overlapping MEMORY areas starting after a specified previous MEMORY area, for BSS specific to one of several mutually exclusive parts of a program): I'll need to make a demo of resolving this with __*_LAST__
  • Union segments part II (MEMORY area starting after the longest of multiple overlapping MEMORY areas): Not resolved yet
  • SEGMENT aligned to end within its MEMORY area: Not resolved yet

I wonder whether scratchpad variables in $0000-$001F could be defined with a .struct.


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  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Bing [Bot] and 12 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