6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 29, 2024 12:22 pm

All times are UTC




Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Wed Nov 27, 2019 2:03 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
I've recently started writing some 6502 code where I'm likely to want multiple configurations of an application both for different platforms (e.g., Apple II, C64, VIC-20, KIM-1 etc.) and for vastly different memory configurations (4K RAM only, 4K ram plus 4-12K ROM, "huge" RAM of 32K plus for both RAM-only and RAM+ROM configurations, etc.) This would involve builds both including just certain platform-specific code from many options, such as keyboard/screen/terminal handling, and builds that include or exclude chunks of otherwise common code between systems, such as something like floating point functions.

I also want macros and the ability to assemble for different CPUs (particularly 6800, 6809, 8080, Z80 and RCA 1802) and so for this and the above reason I settled on the ASxxxx assembler suite, which produces relocatable modules then linked with `aslink`.

However, I'm not all that happy with ASxxxx: the syntax isn't to my liking (particularly that temporary/local labels are restricted to numbers), I seem to be encountering issues getting it to properly generate zero-page instead of full-address instructions automatically, and the whole thing seems generally just a bit baroque.

Unfortunately, there aren't many multi-platform macro assemblers that use a linker (are there any others?). But I'm wondering now how helpful a linker really is over simply creating source files that include other files. Potential downsides are:

  • Local vs. global symbols: assemblers such as AS offer SECTION/ENDSECTION directives that seem as if they would do just as well, perhaps even better because there are more levels of scope.
  • :Size-limited sections/areas: ASxxxx lets you do `.area foo (PAG)` to create an area that will give a warning/error if the size exceeds 256 bytes, which can be used for checking that, e.g., the direct page allocations of several different modules don't overflow the direct page. I'm not sure how one deals with this in whole-program assemblers, or if they even offer this kind of facility.
  • Speed: probably not an issue given how fast modern systems, how fast assembling (as oposed to compiling a higher level language) is, are and that I'm unlikely to be assembling anything larger than about 10,000 lines of code, at most. It's not as if the linking/relocation stage doesn't take time anyway.
  • ???: Some other things that I'm missing?

Does anybody have any experience with or thoughts on this? Am I possibly just lacking too much understanding of the whole idea of largish projects in assembly language to see if I'm worrying about the wrong issues, or missing something more important?

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 2:50 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
An advanced topic! Good questions and it will be interesting to see answers.

(On the Beeb, with large projects it's not uncommon to have multiple source files and there are tactics for exporting and importing symbols. The assembly language is embedded within Basic and so there are some higher-level facilities. Thinking about it, there are BBC Basics for several micros so just possibly that's a useful observation. But it isn't the kind of thing you thought you wanted!)


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 3:04 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1467
Location: Scotland
ca65 can probably do all you want. It takes a platform configuration file which can specify where various code and data sections are to live - although the actual loading of the binary code into the target system is sometimes left as an exercise to the user.

[edit to say that I initially missed the bit about cross platform - ca65 is 6502/65816 only, sorry]

Adding a new platform into it is tricky - mostly because there isn't much documentation, but it involves changing some of the source code files. However that's really aimed at the C part of it all. For pure asm, then it's not that relevant.

cjs wrote:

  • Local vs. global symbols: assemblers such as AS offer SECTION/ENDSECTION directives that seem as if they would do just as well, perhaps even better because there are more levels of scope.
  • :Size-limited sections/areas: ASxxxx lets you do `.area foo (PAG)` to create an area that will give a warning/error if the size exceeds 256 bytes, which can be used for checking that, e.g., the direct page allocations of several different modules don't overflow the direct page. I'm not sure how one deals with this in whole-program assemblers, or if they even offer this kind of facility.
  • Speed: probably not an issue given how fast modern systems, how fast assembling (as oposed to compiling a higher level language) is, are and that I'm unlikely to be assembling anything larger than about 10,000 lines of code, at most. It's not as if the linking/relocation stage doesn't take time anyway.
  • ???: Some other things that I'm missing?

Does anybody have any experience with or thoughts on this? Am I possibly just lacking too much understanding of the whole idea of largish projects in assembly language to see if I'm worrying about the wrong issues, or missing something more important?


ca65 was designed to be the back-end assembler to the cc65 C compiler, however it's very good on it's own. I may be biased though, but I use it to build everything I do on my Ruby 6502 and 65816 systems. My OS for the '816 is split over 70+ files (.s and .h), about 13,000 lines, and uses a Makefile to build. It assembles and links in under a second on my somewhat modest i3 Linux desktop PC.

On the size limited thing - it's not automatic, but you can flag warnings with conditionals, so e.g.

Code:
commandTable:
        .asciiz "LS"            ; Has to be the first for histerical reasons...
...stuff omitted
        .asciiz "EXIT"
        .byte   0
;       .byte   commandTableEnd-commandTable    ; un-comment to find out how big it is in the .lst
commandTableEnd:

        .if commandTableEnd-commandTable > 250
                .error "Command table too big"
        .endif


The local/global stuff is supported - either separate files, or by using .proc/.endproc for functions. This lets you use the same label names inside each 'proc' if you like. Think of each proc like a C function. You have to export symbols to make them global and import them in other files you want to use them (again, think C extern variables)

I build my OS with the platform set to 'none' but I created a platform for C programs which does stuff like set the start/load address ($8000) and the start and end of the data segment, stack and so on, and how much zero page it can use. you don't need much of this for most asm projects though.

Start here: https://cc65.github.io/index.html

Cheers,

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 3:36 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
My assembler suite Dev65 is cross platform (anything with a JVM/JRE) and does all the CPUs you mention except Z80 (it could be added). It has macros, is relocatable, has a linker and temporaries start with a full stop (e.g. .loop, .this65)

https://github.com/andrew-jacobs/dev65/tree/master/examples

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 4:09 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
Sounds like just the thing!


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 4:24 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
drogon wrote:
ca65 can probably do all you want.

ca65 is great; I learned a fair amount about it when I contributed a bug fix for the conio library on the VIC-20. As you noticed, though, it isn't cross-platform. That may not be a killer; I could well be better off just living with several different assemblers than trying to use one for everything, though that creates more work elsewhere. (Particularly in my unit test framework, where I already spent more hours than I cared to writing code to dredge non-exported symbol addresses out of the ASxxxx listing files because they're simply not available anywhere else outside of the linker's memory.)

That does remind me of another feature I'd forgotten: cl65 can build tables of specifically marked functions for things like library constructors and destructors, which is pretty cool and could be quite handy.

Quote:
On the size limited thing - it's not automatic, but you can flag warnings with conditionals....

I was thinking that something like that might be possible, so it's good to know that someone's done it and it works!

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 4:41 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
BitWise wrote:
My assembler suite Dev65 is cross platform (anything with a JVM/JRE) and does all the CPUs you mention except Z80 (it could be added). It has macros, is relocatable, has a linker and temporaries start with a full stop (e.g. .loop, .this65)

Looks interesting, and that's definitely the way I like reusable labels to be done. But I notice you don't seem to have any kind of export directive; are all non-resuable labels exported as symbols global to the entire application being linked? (I.e., if I have a routine labeled `alloc` that's used only within one file and intended to be local to that file, will that collide with a different `alloc` used in another file?)

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 27, 2019 7:24 pm 
Offline

Joined: Mon Nov 18, 2019 8:08 pm
Posts: 9
I collect platform specific code to separate files which are conditionally included depending on the target. These contain subroutines, macros, and various constants. Mostly the same symbols in all just different code, content or values.

There might be a few conditionally compiled parts in the common code as well depending on how worried I'm about space or optimizations.

I control the memory layout through the same conditional compilation as everything else. In large batches of course like initialized/uninitialized variables, common/banked code, etc. All sources are compiled for a target at once, no export files, linking and other distractions. However this is toolchain and personal work style dependent.

I have no idea what this project is going to do but based on the CPU list it sounds like it'll be written almost entirely with macros with the exception of the platform dependent code. If so one could throw in a bytecode interpreter as well to cut down on memory use. If it'd help or make it harder depends.

For me it was usually easy as the platforms were either all using the same 6502 CPU and were just different CBM machines or were using different 65xx variants. For the latter I had some macros to output optimized code for the target like phx vs. txa+pha.


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 28, 2019 12:17 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
cjs wrote:
BitWise wrote:
My assembler suite Dev65 is cross platform (anything with a JVM/JRE) and does all the CPUs you mention except Z80 (it could be added). It has macros, is relocatable, has a linker and temporaries start with a full stop (e.g. .loop, .this65)

Looks interesting, and that's definitely the way I like reusable labels to be done. But I notice you don't seem to have any kind of export directive; are all non-resuable labels exported as symbols global to the entire application being linked? (I.e., if I have a routine labeled `alloc` that's used only within one file and intended to be local to that file, will that collide with a different `alloc` used in another file?)

My assemblers have .GLOBAL to export a symbol to other modules and .EXTERN to import one. All other symbols are local to the module they are defined in.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 28, 2019 1:24 am 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
BitWise wrote:
My assemblers have .GLOBAL to export a symbol to other modules and .EXTERN to import one. All other symbols are local to the module they are defined in.

Ah, so it was just a issue between the documentation and my approch to it. In the as65 documentation page I did search for "external," which didn't get me to the `.EXTERN` directive, and "global" seemed to refer to resuable labels from the first few search results so I didn't continue searching further, though that would have found the `.GLOBAL` directive later on. Obviously I could have done a better job searching, but it might make sense to use a term other than "global" for the resuable labels.

It might also make sense to re-organize the documentation into sections describing the various facilities, rather than listing all the directives together in one section, and add a small table of contents to the top to help readers get an overall sense of what your assembler offers. In general, I find the documentation could do better in terms of the order and grouping of presentation, though that may just be me skimming too fast.

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 28, 2019 12:29 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
So I think I've just discovered a notable disadvantage to linking as compared to whole-program assembly. It turns out that ASxxxx does not support automatically using zero-page addressing instead of absolute; for instructions that have both addressing modes you must specify zero-page addressing if you want it, otherwise absolute addressing will always be used.

Thinking about it, this makes some sense. Of course for symbols declared within the file it's easy enough to tell that they're in the zero page, but for external symbols there would need to be a declaration somewhere to indicate at assembly time, long before the link stage, that it's a zero page address, which starts to introduce all sorts of potential pain. Or am I missing some easy way to do this?

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 29, 2019 3:04 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
The WDC convention is a use prefixes to the address to force addressing modes when the value of the address expression is unknown (i.e. an EXTERN symbol)

Both WDC and my assembler allow:
Code:
lda <addr    ; Forces zero (direct) page
lda !addr     ; Forces absolute
lda |addr     ; ditto
lds >addr    ; Forces long absolute (for the 65C816)

You can add indexes as a suffix. Indirect modes imply the address is zero page except in JMP instructions where its absolute.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 29, 2019 5:41 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
I've discovered perhaps the source of my confusion on the zero-page thing: going back through my older code to explicitly mark zero-page loads/stores/etc., I've discovered that ASxxxx did do the optimization (without being specifically asked) when the zero-page-resident labels were declared in the same (absolute) segment as the code using them. That was some of the first code I'd written, when I was still monitoring the listing files quite closely and looking at the machine code produced, so I'm guessing I noticed it was doing the right thing there and assumed it always would.

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ] 

All times are UTC


Who is online

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