News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: 65816: Direct Page vs Absolute Operand  (Read 18317 times)

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
65816: Direct Page vs Absolute Operand
« on: May 17, 2016, 12:44:23 pm »
Although I'm familiar with assembly generally and, decades ago wrote 6502 code for the Apple II, I am a bit conflicted upon reading the WDC manual and some disassembly examples as it relates to direct page and absolute operands. Take the following example as input to an assembler:

Code: [Select]
LABEL EQU $50
      LDA LABEL

Would you interpret that as a direct page operand? Or an absolute operand? Would it make any difference to you if I wrote it this way:

Code: [Select]
LABEL EQU $0050
      LDA LABEL

(Or "LABEL = $50", if you prefer.) I'm pretty sure I know what the responses will be regarding this following example:

Code: [Select]
LABEL EQU $1A50
      LDA LABEL

But I'm wondering if the assembly writer has any control here. Is it simply the case that the assembler examines the upper byte(s) of the symbol to see if it should be taken one way, or another? It seems to me that I should be able to force the interpretation to go the way I want it to go, regardless of the assemble-time or link-time value of the symbol. But I'm curious what experiences people have, with various assembly tools, on this narrow point.

A similar question might be posed regarding the 'absolute long' mode available with the 65816, as well. But I'll hold short of that, for now. Answering the above question may be sufficient.

(I apologize for being a little lazy, today. But I'd rather not go out and install a variety of different assembler tools, learn how to use them, write code to test the above idea, and find out that way... when someone here may be able to quickly tell me what their experience says already about this. I did do a bit of googling this already, without finding anything specific enough to be sure about it.)
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Revenant

  • Full Member
  • ***
  • Posts: 206
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #1 on: May 17, 2016, 12:55:58 pm »
Most of the time a two-digit literal address (like $50) would be interpreted as a direct page address rather than a normal absolute one. However, most assemblers have a way to specify a specific address size (e.g. "lda.w $50" would read the absolute address $0050, and likewise "lda.l" for a long address). The syntax varies from one assembler to another.

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #2 on: May 17, 2016, 12:56:31 pm »
Most assemblers allow the programmer to explicitly determine the size of the instruction.  I'm not sure how WDC does it.

But unless the programmer explicitly specifies, it's up to the assembler to determine the size.  And many assemblers are pretty lousy in this area and don't give the programmer the control they should to take advantage of direct page effectively. Which is partly why I wrote my own a while back that I never really released.

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #3 on: May 17, 2016, 02:14:30 pm »
Most of the time a two-digit literal address (like $50) would be interpreted as a direct page address rather than a normal absolute one.
That would be a problem when using symbols which may not only be defined elsewhere but also might possibly not be fully determined until link-time. The assembler wouldn't even know the size of the instruction as assembly-time!

However, most assemblers have a way to specify a specific address size (e.g. "lda.w $50" would read the absolute address $0050, and likewise "lda.l" for a long address). The syntax varies from one assembler to another.
Hmm. Okay. So, some assemblers (not necessarily all) have a way of over-riding an otherwise possibly ambiguous interpretation? How exactly then can they compute the appropriate DP-relative value, given ONLY a label and NO INFORMATION at all about the current DP value? Just curious.



May 17, 2016, 02:26:15 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Most assemblers allow the programmer to explicitly determine the size of the instruction.  I'm not sure how WDC does it.
But it's not exactly the 'size', though of course that is implied. But instead the semantic meaning of the operand.

For example, let's assume we are talking about a symbolic assembler and the symbol under discussion is a 24-bit address. This may be accessible via DP, or not. The assembler would need to be told the current value of the DP (as known by the code writer, of course) in order to figure this out. In fact, it would need to know that in order to figure out the remaining offset value. So an assembler would need a pseudo-op so as to tell it the current DP value. Forcing the assembler writer to have to keep track of the relative offsets seems pretty stupid to me, in fact.

The assembler might, knowing DP and knowing the symbolic label value, find that it the label cannot be reached through the DP value. But that, perhaps, it can be reached via bank 0. In that case, an absolute address would be perfectly fine. Not being able to reach the label either by DP or by bank 0 should, of course, produce an error.

I'm a little bit bothered by what I'm seeing in the disassemblers. But I'm even more bothered by what I see as an apparent complete lack of a mechanism by which to keep the assembler up to date on the important register values (DP, for example.) It seems to be impossible for an assembler to accept a valid label, all of which technically possess a 24-bit address, and to then be able to compute the DP-relative offset for it so that a valid LDA via DP can be computed and applied.

But unless the programmer explicitly specifies, it's up to the assembler to determine the size.  And many assemblers are pretty lousy in this area and don't give the programmer the control they should to take advantage of direct page effectively. Which is partly why I wrote my own a while back that I never really released.
I'm not even certain, though, that an assembler can even COMPUTE the correct DP-relative offset, given a label. The programmer, obviously, can "hand-calculate" the offset. But that is STUPID to require in all cases. It may be common practice. But it is still stupid. The assembler should be able to tell, at assembly time, if the label is reachable by the DP and, if so, what the appropriate offset is, relative to the DP, so that it can encode the instruction correctly. The programmer shouldn't have to go to some piece of paper, or have to remember.

I'm thinking about a fully relocating assembler here.
« Last Edit: May 17, 2016, 02:30:12 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #4 on: May 17, 2016, 02:49:39 pm »
But it's not exactly the 'size', though of course that is implied. But instead the semantic meaning of the operand.

To-may-to/to-mah-to.  Direct Page / Absolute / Long all have the same functionality, they just specificy a different number of bits of the target address... with the remaining number of bits filled in by registers.

Quote
Forcing the assembler writer to have to keep track of the relative offsets seems pretty stupid to me, in fact.

I agree.  That's why I put direct page directives in my assembler (Schasm).  It is sorely lacking in others.

Quote
Not being able to reach the label either by DP or by bank 0 should, of course, produce an error.

Agreed.  You actually are summing up all the reasons I wrote my own assembler.  =P  It was primarily to address all the same things you're talking about.  None of the existing assemblers seemed tailored for developers focusing on symbols rather than raw addresses.

Quote
I'm not even certain, though, that an assembler can even COMPUTE the correct DP-relative offset, given a label.

It's easy if the label preceeds the reference (so the address is known at the time of the reference), but gets surprisingly complicated if the label is AFTER the reference (since the address depends on, among other things, the size of the very instruction you're trying to assemble).  I think I settled on defaulting to absolute for unresolved labels and erroring if it's discovered they needed long addressing once the label is resolved.


You're actually making me want to pick up development of Schasm again.  I'm very interesting in having a good cross assembler for 65xx series that supports symbolic debugging and isn't extraordinarily clunky like many others are.  I stopped development because I was unhappy with how I approached macros and didn't want to redo it... but if you're interested, maybe we could hash out ideas for a feature set for an assembler?

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #5 on: May 17, 2016, 03:03:18 pm »
You're actually making me want to pick up development of Schasm again.  I'm very interesting in having a good cross assembler for 65xx series that supports symbolic debugging and isn't extraordinarily clunky like many others are.  I stopped development because I was unhappy with how I approached macros and didn't want to redo it... but if you're interested, maybe we could hash out ideas for a feature set for an assembler?
Yes. Count me in, if you'd like any help.

I can pick up any of the parts you don't want to deal with, with your existing tool. For example, I'm quite adept at expression parsing, symbolic algebraic manipulation, and application of rules towards optimization/simplification. That can be a separable part. But you may already have all you need there, too. No idea.

Or we can actually just do a ground-up design, if you'd like. Personally, I'd like an assembler/linker system that provides fully abstracted "segments" which support code, data, and even can overlap or overlay segments on top of each other (in effect, a segment 'union'.) At the link phase, I'd like to be able to place code as I see fit, without regard to where the assembler "thought" it belonged. (In short, I'd like to be able to assemble code "overlays" which can be banked into the address space through mapping hardware.) Macro processing is a bit of a question right now for me. I'd like an invoked macro to be able to switch from the current code segment to a previous data segment, drop down some data bytes with a macro-generated label, and then pop back up into whatever the current code segment happens to be for adding code that references this newly generated data label. There are dozens, if not hundreds, of other small details that can make a huge difference in coding practice. I'm still on the fence regarding link-time instruction sizing -- pc-relative branching is an example, where one isn't forced to make a decision about BRA vs BRL while writing code, but instead allows the assembler to determine which one will work. I'm okay not having the linker do that. But I'd like to explore it (and a few other related options) just to make sure it's the right decision to not do this at link time.

On the "clunky" point you make, that's a matter we'd need to discuss. I'd very much like to press towards something elegant and as simple as possible to understand and use well. I want people who don't know how to use assemblers well to be able to pick it up and run with it. But I'd also like to have features targeted for those who have grown past the early learning stages and need support for truly large projects, too. I don't know exactly what that may mean, yet. I'm still just wrapping my mind around all this.

(Something very, very strange has occurred to me, too: should an assembler macro processor, let's say, be able to read data bytes directly from a ROM being "patched" in order to 'modify' bit-mapped table values? This comes up in patching DQ3, for example, where I provide an example of extending the 'gold' display to also show the field/town X and Y location. You know you want to increase the height by two and to move it upward on the screen by two, but you don't care where it is currently located or what its current size is -- you just want to examine, extract, and then modify those bit fields and you don't want to "hard code" knowledge of the screen locations or the display box size in your assembler source code. It's really odd. I admit it. But it crosses my crazy mind, anyway.)

Regardless, sure... count me in if you'll have me.
« Last Edit: May 17, 2016, 03:21:15 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #6 on: May 17, 2016, 03:49:49 pm »
Yes. Count me in, if you'd like any help.

Most of what I'd want help with is ideas for practical features.  It's probably easier for me to crank out the code myself than it would be to figure out how to split it across two developers (at least for something of this complexity).

Quote
Or we can actually just do a ground-up design, if you'd like.

I'd prefer this.  I can borrow pieces from the old assembler and work them into a new one -- but it would be best to start with a fresh design that has all the features we'd want.

Quote
Personally, I'd like an assembler/linker system that provides fully abstracted "segments" which support code, data, and even can overlap or overlay segments on top of each other (in effect, a segment 'union'.) At the link phase, I'd like to be able to place code as I see fit, without regard to where the assembler "thought" it belonged. (In short, I'd like to be able to assemble code "overlays" which can be banked into the address space through mapping hardware.)

I have a few concerns with this approach:

-  It makes the assembler much more difficult to use.  ca65 is a great example of this -- that is a fantastic assembler, but you can't just write a file and assemble it.  You have to juggle config files, set up segments, and actually doing a full build is a multi step process.  It's much more complicated than it needs to be, and it turns a lot of people away from using it.

-  It complicates "code injection" style assembling.  xkas is popular because you can write a small file, and run it to assemble and inject that code into an existing ROM.  Practical, simple, and easy.  Introducing a linker phase almost makes the assumption that the linker has full control over the output file -- which is a bad assumption to make.

-  I'm not sure the benefits are worth it.  Having relocatable segments is a very specific feature for something that could very easily be done in the assembler by way of directives.  Give them an ORG directive to indicate the PC, give them an OFFSET directive to indicate a file offset.  If they want to move stuff around, it's as simple as tweaking a constant in their code.  No need for linker bloat.


Linkers make sense for HLLs but I think they're overkill for this kind of assembler.

Quote
Macro processing is a bit of a question right now for me.

I'll have to reread docs of how existing assemblers handle macros.  I originally took an overly restrictive approach where each macro had to take a fixed number of arguments, and the arguments had to be complete symbols.

Quote
I'd like an invoked macro to be able to switch from the current code segment to a previous data segment, drop down some data bytes with a macro-generated label, and then pop back up into whatever the current code segment happens to be for adding code that references this newly generated data label.

Pushing & popping assembler settings was something I definitely added in my first go of Schasm.  So yeah I see the value here.  Really macros are just text substituion with some scoping rules.  Any features you want them to have apart from that could probably be accomplished through other directives.




My previous assembler is here:   https://www.dropbox.com/s/wsivt454ucd884q/schasm_alpha002_2015_07_16.zip?dl=0

Documentation is included.  If you want to review the docs and give feedback as to improvements that would be a big help.  The only real big changes I can think of would be:

- Redo macros completely.  Look into macros accepting a variable number of arguments
- Add a directive to repeat a block of code a given number of times
- Support for structs
- Support for fixed-point  (need good syntax for this)

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #7 on: May 17, 2016, 04:10:34 pm »
Yes. Count me in, if you'd like any help.
Most of what I'd want help with is ideas for practical features.  It's probably easier for me to crank out the code myself than it would be to figure out how to split it across two developers (at least for something of this complexity).
Sounds fine. I can certainly kibitz with the best of them.  ;)

Or we can actually just do a ground-up design, if you'd like.
I'd prefer this.  I can borrow pieces from the old assembler and work them into a new one -- but it would be best to start with a fresh design that has all the features we'd want.
Your call. I'm just going to be kibitzing. :)

Personally, I'd like an assembler/linker system that provides fully abstracted "segments" which support code, data, and even can overlap or overlay segments on top of each other (in effect, a segment 'union'.) At the link phase, I'd like to be able to place code as I see fit, without regard to where the assembler "thought" it belonged. (In short, I'd like to be able to assemble code "overlays" which can be banked into the address space through mapping hardware.)
I have a few concerns with this approach:

-  It makes the assembler much more difficult to use.  ca65 is a great example of this -- that is a fantastic assembler, but you can't just write a file and assemble it.  You have to juggle config files, set up segments, and actually doing a full build is a multi step process.  It's much more complicated than it needs to be, and it turns a lot of people away from using it.

-  It complicates "code injection" style assembling.  xkas is popular because you can write a small file, and run it to assemble and inject that code into an existing ROM.  Practical, simple, and easy.  Introducing a linker phase almost makes the assumption that the linker has full control over the output file -- which is a bad assumption to make.

-  I'm not sure the benefits are worth it.  Having relocatable segments is a very specific feature for something that could very easily be done in the assembler by way of directives.  Give them an ORG directive to indicate the PC, give them an OFFSET directive to indicate a file offset.  If they want to move stuff around, it's as simple as tweaking a constant in their code.  No need for linker bloat.

Linkers make sense for HLLs but I think they're overkill for this kind of assembler.
There is a sneaky way to handle this. Each source file can be treated as a "segment." The coder won't even need to know it is happening, if they don't care to know. I have an example of this to show, if you care to see it. But I'm also perfectly willing to see what you already have (you mention some things I should read) and see how that applies to what I care about. If it doesn't help me, it doesn't. If it does, then fine. I move forward and don't look back.


Macro processing is a bit of a question right now for me.
I'll have to reread docs of how existing assemblers handle macros.  I originally took an overly restrictive approach where each macro had to take a fixed number of arguments, and the arguments had to be complete symbols.
Okay. Look that over. I can easily provide some "tough cases" for you to consider supporting. Just to push you a bit. :)


I'd like an invoked macro to be able to switch from the current code segment to a previous data segment, drop down some data bytes with a macro-generated label, and then pop back up into whatever the current code segment happens to be for adding code that references this newly generated data label.
Pushing & popping assembler settings was something I definitely added in my first go of Schasm.  So yeah I see the value here.  Really macros are just text substituion with some scoping rules.  Any features you want them to have apart from that could probably be accomplished through other directives.
The directives/pseudo ops should be able to be pushed/popped, where it makes sense to do so. You might, for example, push the current state of the accumulator size or index size. Macros may need this. But since you aren't thinking towards segments perhaps this isn't anything you'd see a reason to care about.


My previous assembler is here:   https://www.dropbox.com/s/wsivt454ucd884q/schasm_alpha002_2015_07_16.zip?dl=0

Documentation is included.  If you want to review the docs and give feedback as to improvements that would be a big help.  The only real big changes I can think of would be:

- Redo macros completely.  Look into macros accepting a variable number of arguments
- Add a directive to repeat a block of code a given number of times
- Support for structs
- Support for fixed-point  (need good syntax for this)
Thanks. I'll see about helping push your envelopes. ;) I definitely WANT something for structs! So put a special mark on that one!


As a final note, just a comment on expression analysis. Your parser should be able to recognize that (LABEL1 - LABEL2) isn't an address, but instead is a constant representing the span. Adding a span to an address label is another valid address label. Adding two memory address labels is "bad." At least, if not otherwise compensated later by a subtraction. Etc. Semantic context during expression analysis is helpful. I usually keep track of such semantic details during expression analysis and simplification.



May 17, 2016, 04:30:31 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
I'd love to see how you handle those for operand expressions. For example, may I do this?

Code: [Select]
      ASSUME    DP = MyTable
      LDA          MyTable[5 * SIZEOF MyTableEntryStruct].EntryMember

And have it compute a DP-relative expression, (5 * SIZEOF MyTableEntryStruct + OFFSET MyTableEntryStruct.EntryMember), as the operand value for an 0xA5 LDA instruction?

Structs are cool!

Hmm. Bit-fields, too! That means we'd need a MASK operator to automatically generate the mask bits for the bit fields. I'm loving this, already!



May 17, 2016, 05:02:53 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
I don't have a dropbox account and really don't want to set one up, either. You open to just sending me the file, directly?
« Last Edit: May 17, 2016, 05:03:21 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #8 on: May 17, 2016, 05:23:45 pm »
I had to look up what 'kibitzing' was.  Hah.

---------
Regarding a linker phase:

I can see the value in separating the build process into multiple steps for large projects, so you don't have to assemble the entire file each time.  An interface for that is worth working in -- and such an interface would imply intermediate object files.

The question then becomes... does the linker decide what address/offset to put code at?  Or does the assembler?

I shudder at ca65's approach.  It has an ORG directive, but it's effectively useless because the linker determines the ultimate address.  And I also want something like ORG to be available because it's familiar and simple.  So I would lean towards everything being determined by the assembler.  Object files would consist of the final assembled output, as well as a list of exported symbols, and a list of symbols that need to be imported and "plugged into" the assembled code.

Furthermore, I would not want the two-step process to be the default behavior for the assembler.  I would imagine most use cases are going to be one-shot (even one-file) assemblies.

If we don't want to go with that idea, how would you want the linker to determine the final address/offsets?


---------
Regarding structs:

Syntax for structs is weird, and I'm not sure how I'd want to tackle it.  A lot of interfacing with structs requires the coder to manually compute an index (typically by left-shifting the ID several times):

Code: [Select]
; assuming the struct is 2 bytes wide
;
; data stored interleaved as:  foo/bar/foo/bar/foo/bar

lda desired_index
asl A
tax

lda mystruct.foo, X
sta mystruct.bar, X

Personally I hate structs and never use them in assembly, since they require that additional shifting which also means larger indexes, more page crosses, and often padding to space the struct to a conveinient size which would otherwise be completely unnecessary.

I've never found a situation where I would prefer a struct to storing each field in their own array.  Something like this:

Code: [Select]
; no struct, data stored in own array, such as:   foo/foo/foo/bar/bar/bar

ldx desired_index
lda foo,X
sta bar,X


This is why I didn't support them initially.  But apparently structs are a popular feature?  Barf.

I'm lost on this one.  Since this is not something I would use, I have no idea what kind of syntax I would want for it.  How would you want the syntax for structs to look?




----------
For bit fields:

???   huh???



EDIT:

You shouldn't need a dropbox account to download the file.  Just follow the link and click the download button.

Unless dropbox changed?

But yeah I don't mind sending it by email or something.  Send me a PM with your email address and I can shoot it over.




EDIT 2:

Also I forgot to mention this:

My assembler didn't make a distinction between labels and other numeric constants.  As far as it's concerned they're all just numbers, so adding two labels together would be meaningless but would be completely legal.

Adding what is effectively type safety to numeric values seems like overkill.  What benefit would it have other than guarding the very rare mistake of adding two labels?

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #9 on: May 17, 2016, 06:12:08 pm »
I had to look up what 'kibitzing' was.  Hah.
Hehe. I love it someone else is willing to do all the work and just let me sit in the back seat and kibitz about their driving habits. ;) Frees me up to be truly annoying.

Regarding a linker phase:

I can see the value in separating the build process into multiple steps for large projects, so you don't have to assemble the entire file each time.  An interface for that is worth working in -- and such an interface would imply intermediate object files.
A few weeks ago, I would have agreed with this. But then I saw the Merlin32 assembler code. The darned thing loads up all of the assembly source files -- and I mean ALL OF THEM -- and linked-list chains up the lines in each of them. It then linked-list chains up groups of them into segments. (You can include multiple ASM files in a grouped segment.) It then linked-list chains up the groups into the total project. It goes through each of the individual lines, assigning addresses and tossing out opcodes and the like. Each line is its own tiny "code byte stream." At the total project level, he has another chained list of code and data patches, that the assembly-phase added when it couldn't resolve things then, so that the link phase knows what to "patch in" later on. Once all that is done, each of the individual lines has a fully patched in and decoded "byte stream." (The instructions are never longer than 4 bytes there. But the data can be longer, of course.) The whole thing doesn't care about the physical memory system, or how any of that maps to a ROM, at this phase. It's just a whole lot of individual lines, each with their own ORG address plus a tiny strip of attached data to it. It's only at this point that his code decides how to do output. Note that you can ORG to some place, generate a little bit of code, ORG to somewhere else, generate a little more, etc. So far as these structures care, it doesn't matter if you have 100 separate lines all with the same ORG and different data. It just doesn't matter because none of it understands anything about the memory address space or the ROM. It's just "ORG + BYTES" everywhere.

I had a very easy time jumping into that thing and making it patch a ROM file directly and to support any kind of mapping hardware.

I'm not suggesting you consider using that thing. I'm just pointing out that it didn't need to support separate compilation or object file formats. It's really easy from a user point of view because they don't have to know about separate compilation or object files. So far as they know, they have a project with some ASM files and somehow it all just works right. No binary object files are generated. They just need to have a basic project file listing the sources. (Kind of a make file, I suppose.) The rest just happens.

The question then becomes... does the linker decide what address/offset to put code at?  Or does the assembler?
Yeah. That's the question. I can offer only some modest thoughts.

Since symbols can be external, expressions involving them can only be resolved at link-time. If you are going to support externals, then I think you are stuck with that fact. This would mean to me that while the assembler can do some of the constant folding semantics of an expression, so long as the expression carries any external reference in it, you have to defer final computation it until link-time. That implies retaining a reduced expression tree. (It may involve two or more externals in the expression, so what choice do you have then?)

Also, if you are going to support letting the assembler "know about" the DP value, it's possible that the DP expression (the one used in the 'assume' directive the assembler uses to keep track of DP) itself contains externals! So, again, the assembler can't possibly know at assembly-time how to use the assumed-known DP in the context of an LDA instruction that may also reference that external, for example, where the two need to be 'differenced' later to compute the LDA value. So once again, that has to be deferred until link-time. All the assembler can do is to correctly construct the expression tree to be resolved at link-time.

Note that if you do this right and well, a user won't even know all the trouble you are going to, here. All they see is that this somehow always "just works right." And they shouldn't have to care, either. It should just work right.

I shudder at ca65's approach.  It has an ORG directive, but it's effectively useless because the linker determines the ultimate address.  And I also want something like ORG to be available because it's familiar and simple.  So I would lean towards everything being determined by the assembler.  Object files would consist of the final assembled output, as well as a list of exported symbols, and a list of symbols that need to be imported and "plugged into" the assembled code.
At some point, I'd like to expose you to that bit of "hacking" I did to the Merlin32 tool to make ASMPATCH. Not because I want you to use it, but because I want you to see how easy it is to specify a patch to a ROM so that you can see if there is anything useful to learn from such examples. I don't know if there is, actually. I'd just like to hear your opinion after seeing an example or two. It might shake out an idea from you.

Furthermore, I would not want the two-step process to be the default behavior for the assembler.  I would imagine most use cases are going to be one-shot (even one-file) assemblies.
Yes, I'd like to see something very, very easy to use in the common case of just one assembly source file with patches in it. That's the example I give on the ASMPATCH web link, in fact. Very simple to do. Just works.

If we don't want to go with that idea, how would you want the linker to determine the final address/offsets?
Well, let's exchange more thoughts before I answer here. If it is deferred to link-time, I think it would be "hacked" if there is no expression tree to process at link-time. The Merlin32 tool I started out modifying is really dumb, this way. It ONLY knows about one external and one constant offset to it. And it is really kludgy, as a result. I don't like that. So that would seem to spell out a reduced expression tree presented to the linker, I think. But that's only if you defer things until then. If not, none of it matters.

Regarding structs:

Syntax for structs is weird, and I'm not sure how I'd want to tackle it.  A lot of interfacing with structs requires the coder to manually compute an index (typically by left-shifting the ID several times):

Code: [Select]
; assuming the struct is 2 bytes wide
;
; data stored interleaved as:  foo/bar/foo/bar/foo/bar

lda desired_index
asl A
tax

lda mystruct.foo, X
sta mystruct.bar, X

Personally I hate structs and never use them in assembly, since they require that additional shifting which also means larger indexes, more page crosses, and often padding to space the struct to a conveinient size which would otherwise be completely unnecessary.

I've never found a situation where I would prefer a struct to storing each field in their own array.  Something like this:

Code: [Select]
; no struct, data stored in own array, such as:   foo/foo/foo/bar/bar/bar

ldx desired_index
lda foo,X
sta bar,X


This is why I didn't support them initially.  But apparently structs are a popular feature?  Barf.

I'm lost on this one.  Since this is not something I would use, I have no idea what kind of syntax I would want for it.  How would you want the syntax for structs to look?


----------
For bit fields:

???   huh???
Let me think on this. I don't have a ready answer for you. But there are good examples to be found and examined elsewhere. Have you used Microsoft's MASM/ML assembler? It supports structs, masking of bit fields, and so on. It has a syntax for it. Might be worth a look.

EDIT:

You shouldn't need a dropbox account to download the file.  Just follow the link and click the download button.

Unless dropbox changed?
Well, it hassled me about setting up an account and I didn't see, right off, a way to avoid it. I'll go try, again, just to be sure.

But yeah I don't mind sending it by email or something.  Send me a PM with your email address and I can shoot it over.
If you go to this link, Patching SNES ROMs Directly from Assembly, my address is at the bottom of the page. If I can't get dropbox to work for me, you can send it there.

EDIT 2:

Also I forgot to mention this:

My assembler didn't make a distinction between labels and other numeric constants.  As far as it's concerned they're all just numbers, so adding two labels together would be meaningless but would be completely legal.

Adding what is effectively type safety to numeric values seems like overkill.  What benefit would it have other than guarding the very rare mistake of adding two labels?
Hmm. It's useful in understanding what is being asked during assembly. I gave an example already using that complex LDA to a struct object. There is semantic context there. But there are so many other points that need resolving first (the link-time stuff looms large, which includes a lot of lingering questions still) that I really feel this can wait until I understand your direction better. It would be just "made up," right now, without much context and probably just fall on deaf ears. When I better understand your direction, I may be able to put something interesting into that context, then.

May 17, 2016, 06:16:44 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
About dropbox. GOT IT!  I see what I did wrong. Looks good and I was able to pull it down. Thanks!
« Last Edit: May 17, 2016, 06:53:56 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #10 on: May 17, 2016, 07:03:20 pm »
Quote
But there are so many other points that need resolving first (the link-time stuff looms large, which includes a lot of lingering questions still) that I really feel this can wait until I understand your direction better.

Let's start here then.  The first question we should ask is:

- What do we want a separate linker to accomplish?  What extra functionality would it provide?



To me, the reason for having a separate linker in an assembler is the same as the reason for having one in HLLs like C/C++.  That is, project scalability.  A large project with multiple files need only re-assemble the files that were touched and not the entire project.  Relying on the linker being able to plug together all the intermediate object files.

That's more or less what ca65 uses them for (since it's actually a C compiler as well as an assembler).... but from your description of Merlin's approach, it does not seem to accomplish that at all, as it strings together all the source files before they're parsed.  So for the life of me I can't imagine what value it has.

You've mentioned something about being able to drop "segments" into different places -- but I'm having a hard time grasping what that actually means and how it would be useful.  What is a practical example?

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #11 on: May 17, 2016, 08:12:39 pm »
Let's start here then.  The first question we should ask is:

- What do we want a separate linker to accomplish?  What extra functionality would it provide?
  • The linker creates and maintains a set of separate segment "pages."
  • Each segment or page carries a "string" of sequential bytes emitted by the assembler.
  • Each segment or page carries a list of "fix-ups" that will need resolution once the segments are "located" somewhere by the linker.
  • The linker resolves these "fix-ups" and deals with them by making adjustments to the "string" of sequential bytes carried by each segment.
  • The linker may allow a collection of named segments to be combined into a named group of segments, the group itself otherwise similar to a segment.
  • The linker may support segment/group "copying" services, copying the contents of one to another. This may be useful in the case of Harvard architectures, for example, where the initialized constants are "placed" in RAM but must then be copied into a segment that will be placed into a non-volatile area and later copied during startup. It can also be used for code that will be executed once copied from ROM to RAM, but where it must be assembled "as if" it were in RAM to start.
  • The linker may support overlaying segments (as there may be shared RAM used by code segments that are paged in by memory mapping hardware to execute at different times and where, say, the first part of the RAM area is a shared communication space but where the 'excess' is set aside for overlay specific use.)
  • The linker accepts location information about where to physically place segments and groups. That location information MAY be related to a specific CPU address space --or-- it MAY be related to file offsets in a specific ROM mapping (as I directly do right now with Merlin32.) It doesn't really matter to the linker and shouldn't. It's just so much "linear placement address space" (of any kind you want.) I should be able to place segments, assembled to run in a 24-bit address space, into a 32-bit address space ROM file, for example. With ease. Just name the sourcing 24-bit address space segment, tell the linker where it is to be placed in the 32-bit destination address space, and GO.

To me, the reason for having a separate linker in an assembler is the same as the reason for having one in HLLs like C/C++.  That is, project scalability.  A large project with multiple files need only re-assemble the files that were touched and not the entire project.  Relying on the linker being able to plug together all the intermediate object files.
Well, there is that. Which reminds me of a side-bar:
  • There should be support for link-time constants. This means that you only need to assemble the source file that defines these constants once and that you can then link the rest. You might think, "Sure, that's obvious. What the heck do you think an external symbol is?" And I'd agree. But the point here is that the label should NOT need to be an external symbol tied to an address. I should be able to have it be a simple expression -- a pre-defined constant, for example. So I should be able to create a file that defines the byte code for each graphic symbol in a language (like English) and put all that into one file. That file should not need to be "#include"d into each source file, but instead simply linked into the project with the same result. The linker should patch in the values at link-time.
But separate compilation isn't the only goal. You get modules, separation of implementation details from usage, organization, and a host of things all amounting to "good code practice." It's not just a matter of saving compilation time. In fact, I don't even care about the compilation time. Our computers are so way-too-fast these days, anyway. Merlin32 is really dumb, in that regard. It loads everything into memory and has to resolve all of the symbols every time it starts up. And I don't even care. It's just ... crazy fast ... anyway. What matters is the benefits from organizing, separating, documenting individually in bite-sized chunks, and in modularizing my code. That's largely why I care about it.

That's more or less what ca65 uses them for (since it's actually a C compiler as well as an assembler).... but from your description of Merlin's approach, it does not seem to accomplish that at all, as it strings together all the source files before they're parsed.  So for the life of me I can't imagine what value it has.
Well, it reads the files one at a time and parses them. So each line is already broken into "label text," "opcode text," and "operand test." Plus an operand byte, if appropriate, a data array representing the few bytes it may need, an ORG address, and.. that's about it. Well, a link-next pointer, of course. hehe.

But it was trivial to adapt this to poking ROMs, including those involving mapping hardware. It provides the usual benefits of modularization and good coding practices, too. But it has its own set of warts, too.

Think of Merlin32 more as an "interpreter" that loads up all the modules at once and resolves things in-place. It has all the features of separate compilation, without the complexities of creating object files no one really cares about, anyway. It doesn't have the gain in speed you are suggesting. But then, I think you may have missed remembering the modularization benefits here.

Anyway, it's kind of like that, before it starts thinking about writing to some file, anyway.

You've mentioned something about being able to drop "segments" into different places -- but I'm having a hard time grasping what that actually means and how it would be useful.  What is a practical example?
Lots of examples spring to mind. But I'll avoid Harvard architectures and some oddball von Neumann situations and just focus on the 65C816 and the SNES.

Suppose you have a cartridge ROM image that uses mapping hardware (I have the SD2SNES, for example.) Suppose in this case we want to support a ROM that is 32Mbyte in size. SNES bus addresses from $C00000 to $FFFFFF will be always mapped to the first 4MByte of the 32Mbyte ROM. SNES bus addresses from $400000 to $5FFFFF will be used as an overlay area, though, allowing us to map in any 2MByte ROM segment (aligned on 2Mbyte ROM address boundaries) into that SNES address space, under our software control. Let's say we have 10 of these overlays, using up an additional 20Mbyte of our 32Mbyte ROM. (But only one of them mapped into the SNES addresses at a time.) Our cartridge also has some RAM, which is mapped into the SNES address space starting at $600000 and continuing to $7DFFFF. We have initialized data for some of that RAM that must be placed into the ROM where it is non-volatile and can be copied out into the RAM before starting the program. The variables must appear to be located in the SNES address space, while actually being mapped into the 32MByte ROM's address space for safe keeping. We also have another (up to the remaining 8Mbyte) bit of texture maps and other things also located initially in "hidden" ROM address space, but which we may map into the SNES address space temporarily.

How are you going to tell the linker about all this? I need to create 10 different segments of code, all co-located at the same SNES address space, but positioned differently into the ROM address space.

What I'm doing right now with Merlin32 handles all this without a blip. In fact, it's trivial to specify and easy to read, too. And I can move things around without touching a single line of assembly code.

But Merlin32 has a really bad expression syntax, really bad expression handling, it's macros have a lot to be desired, doesn't support structures or bit fields, and... well... it is bad enough that I'm just this side of writing my own tool. But it got me by with my son working on DQ3. So it's doing its job, for now.

-------------------------

When I looked at the other tools they were hard to use, as you alluded to earlier. It takes an expert to use them. Merlin32 is really easy. But it's syntax is funky and it pretends at separate compilation while being nearly completely unable to produce a decent object file of its own. (In fact, it can't. All it can do is produce a single record type in a specific OMF 2.1 file type, which is close to useless.) It has a lot of problems. So I just junked the output side of it, removing its ability to generate that stupid OMF 2.1 file and removing its ability to write out binary streams (all useless, by the way, if for some reason there is ANY discontinuity in the segment.) I then tapped into all that fabulous data it keeps for each line and just walk through all those myself, patching the ROM directly from that data plus the ROM specification information that I added to the linker-file parsing step. The ROM specification is simple to write and use and provides all I need for now. Except, of course, that the assembler generally is pretty lousy.
« Last Edit: May 17, 2016, 08:27:20 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

elmer

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #12 on: May 17, 2016, 08:34:23 pm »
But I'm wondering if the assembly writer has any control here. Is it simply the case that the assembler examines the upper byte(s) of the symbol to see if it should be taken one way, or another? It seems to me that I should be able to force the interpretation to go the way I want it to go, regardless of the assemble-time or link-time value of the symbol. But I'm curious what experiences people have, with various assembly tools, on this narrow point.

This was all standardized years ago back in the 6502 era. WDC basically took the same approach and extended it for the long addressing on the 65816.

For an immediate value ...

"<" selects the low byte of a 16-bit value.
">" selects the high byte of a 16-bit value.
"^" selects the 3rd-byte or 3rd-and-4th byte of a 32-bit value.

For an addressing mode ...

"<" forces zero-page addressing (i.e. the low byte).
">" forces absolute addressing on the 6502, or long addressing on the 65816.
"|" forces absolute addressing on the 65186.

It's up to the programmer to put things in zero-page or not and then force the correct addressing mode if it is ambiguous. The assembler will always use absolute addressing if the size/location of the label is undetermined (or perhaps long addressing on the 65816).

So "lda <myvar,y" forces zero-page addressing, and "lda myvar,y" will use absolute addressing unless the assembler already knows that the variable is located in zero-page memory.

Now ... if "modern" assemblers don't follow the standards ... then that's their problem.

SNASM658 (the expensive professional kit that I used when developing for the SNES) certainly did.

CA65 certainly supports "<", but I'm not sure about the others, because I haven't needed them yet, and so haven't looked.


That would be a problem when using symbols which may not only be defined elsewhere but also might possibly not be fully determined until link-time. The assembler wouldn't even know the size of the instruction as assembly-time!
Hmm. Okay. So, some assemblers (not necessarily all) have a way of over-riding an otherwise possibly ambiguous interpretation? How exactly then can they compute the appropriate DP-relative value, given ONLY a label and NO INFORMATION at all about the current DP value? Just curious.

They can't ... it's up to the programmer to tell the assembler what addressing mode to use based upon the programmer's knowledge of their own code and where the different linked-segments are going to reside.

It's just a basic (and simple) part of software-development on the 6502/65816 architecture.

It's also why things like the DP don't change very often ... it would be a stupid thing to do and dramatically over-complicate the software development for little benefit.


I'm thinking about a fully relocating assembler here.

IMHO, you're overthinking things here trying to apply a 32-bit programming paradigm to a system with a segmented and specialized memory layout where that paradigm doesn't fit.

Back-in-the-day we just didn't do that.

The banks/segments/org system that CA65 uses is very, very similar to the professional SNASM658 system that people used to build these games in the first place ... including using a linker.


But then I saw the Merlin32 assembler code. The darned thing loads up all of the assembly source files -- and I mean ALL OF THEM -- and linked-list chains up the lines in each of them. It then linked-list chains up groups of them into segments. (You can include multiple ASM files in a grouped segment.) It then linked-list chains up the groups into the total project. ...

Errr ... that's just plain stupid. Except for small projects.

But it's also the way that an old in-house development system that I used worked for developing 8-bit and ST/Amiga games ... until we threw the monster away and bought SNASM.


I shudder at ca65's approach.  It has an ORG directive, but it's effectively useless because the linker determines the ultimate address.  And I also want something like ORG to be available because it's familiar and simple.  So I would lean towards everything being determined by the assembler.  Object files would consist of the final assembled output, as well as a list of exported symbols, and a list of symbols that need to be imported and "plugged into" the assembled code.

Furthermore, I would not want the two-step process to be the default behavior for the assembler.  I would imagine most use cases are going to be one-shot (even one-file) assemblies.

You seem to be thinking of hacking projects.

Professional developers used lots of files, and a linker.

Here's the makefile for just the simple frontend (not the main game code) on a SNES project that got cancelled.

As such, its a simple example of the early-days of a project  ...

Code: [Select]
[SnMake]

d:vectors.obj; vectors.658 shellram.658 equates.658
c:\snasm\snasm658.exe /w /z /l /b64 $! vectors.658,d:vectors.obj,,,x.tmp

d:startup.obj; startup.658 shellint.658 shellram.658 equates.658
c:\snasm\snasm658.exe /w /z /l /b64 $! startup.658,d:startup.obj,,,x.tmp

d:shellprg.obj; shellprg.658 shellram.658 equates.658
c:\snasm\snasm658.exe /w /z /l /b64 $! shellprg.658,d:shellprg.obj,,,x.tmp

d:shelldat.obj; shelldat.658 shellram.658 equates.658
c:\snasm\snasm658.exe /w /z /l /b64 $! shelldat.658,d:shelldat.obj,,,x.tmp

t7:; d:vectors.obj d:startup.obj d:shellprg.obj d:shelldat.obj
c:\snasm\snlink.exe /c /b $! @hockey.lnk,t7:,hockey.sym,hockey.map,x.tmp
!ifdef(debugstr)
c:\snasm\snbug658.exe hockey.sym
!endif

[Debug]
c:\snasm\snbug658.exe hockey.sym

[Eval]
c:\snasm\snbug658.exe /v$$$ hockey.sym
« Last Edit: May 17, 2016, 08:46:45 pm by elmer »

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #13 on: May 17, 2016, 09:09:57 pm »
Unless I'm mistaken.... Harvard architectures are n/a here as 65xx series are strictly von Neumann, are they not?  I don't really have plans/interest in expanding this assembler to other families.

That aside, I'm with you on most of the linker functionality.  But some of it directly conflicts with other practices... particularly this bit at the end of your post:

Quote
How are you going to tell the linker about all this? I need to create 10 different segments of code, all co-located at the same SNES address space, but positioned differently into the ROM address space.

Isn't that exactly what the ORG directive is for?  Specifying the desired SNES address space?  I'm loathed to remove ORG entirely due to its simplicity, effectiveness, and familiarity.  But I also don't want duplicate functionality in the linker -- otherwise you end up with the mess ca65 has on its hands (org technically exists but is virtually useless)

My approach to your SD2SNES problem would be to have separate ORG and OFFSET directives, the former specifying the SNES address as the origin for the code, and the latter indicating the file offset.  IMO this is easier to understand and doesn't require additional config files or complex cmdline args that tell the linker where it has to place that stuff -- because it's already specified in the code.

You can even map out regions of RAM by ORG'ing to $7E0000 (or wherever) and nulling the OFFSET so that nothing is actually output, but symbols are still defined.  I did this with the #var directive in my old assembler:

Code: [Select]
    #Var            name, size                      Declare a variable at given pc
   
Var is used to define variables/registers using the PC rather than by doing symbol assignments
specifically.

'name' is the name of the symbol to define, and 'size' is the size in bytes of the variable
it represents.


        #org $7E0000    ;start of SNES RAM
        #var foo, 2
        #var bar, 2
        #var baz, 2
       
The above code has the same effect as:

        foo = $7E0000
        bar = $7E0002
        baz = $7E0004
        #org $7E0006
       
The advantage to using #var over direct assignments is that it makes it easier to move variables around
and/or insert a var in the middle.


The elegance of ORG in this situation is much more preferable to me personally.


Is there a situation where an ORG/OFFSET combination would not suffice?  Or would be unmaintainable?



Quote
But the point here is that the label should NOT need to be an external symbol tied to an address. I should be able to have it be a simple expression -- a pre-defined constant, for example.

Having it set as an address is something that never even crossed my mind.

I have practically ZERO type checking in my assembler.  Symbols either [ultimately] resolve to a number or a string.  Strings are used exclusively with assembler directives (incbin, etc) and cannot be used in other parts of code.  Labels resolve to a number, constants to a number, etc.

Context matters prior to evaluation so you can properly evaluate the symbol -- but once it's evaluated its type is no longer relevant as far as I'm concerned.  Hence why "Label1 + Label2" is legal, despite being nonsensical.  Both labels have to be fully evaluated before the + can be evaluated, and at that point they're reduced to numbers.  From a parser perspective this is just waaaaaay easier to do.


----------------------------------------

@ Elmer:

You seem to be thinking of hacking projects.

I'm thinking of both.  I want a 2-step assembly process with a linker -- I just don't want it to be the default behavior.  Hacking projects are much more relevant these days than from-scratch homebrews.

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #14 on: May 17, 2016, 10:02:24 pm »
Okay. Let's zero in a bit. I'm not sure I understood your example code as it may apply in the following case.

Let's say I'm trying to patch a binary ROM file. So suppose I want two code segments, call them A and B, that are both assembled for SNES address space at $400000. Each of these code segments are large; $200000 in length (2Mbyte.) Neither of them is in memory at the same time, as the mapper will only be used to map one of them at a time into the same SNES $400000 to $5FFFFF address space. My pre-existing game ROM file is 32MByte in size (clearly larger than the SNES address space) and segment A is to be patched into an existing ROM file at file offset address $01A00000 to $01BFFFFF and segment B is patched into the ROM file at file offset address $01E00000 to $01FFFFFF. (Let's say that is how the two segments I'm modifying are organized.) How might I achieve that? (I can do that already with the existing asmpatch program.) Keep in mind that my hypothetical mapper is a hypothetical FPGA and I can hypothetically set up registers to map the ROM pretty much anywhere I want, whenever I want to. And that the total SNES game cartridge's non-volatile code and data well exceeds the total address space of the SNES in any of its configurations. Which, of course, is why I'm using the mapper (MSU1 or whatever.)

Hacking projects are much more relevant these days than from-scratch homebrews.
I agree with you. Assume we are talking about an existing game using an existing mapper with an existing ROM file that is big and is being "updated" by the assembler/linker's generated output.
« Last Edit: May 17, 2016, 10:17:18 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #15 on: May 17, 2016, 10:17:45 pm »
Neither of them is in memory at the same time, as the mapper will only be used to map one of them at a time into the same SNES $400000 to $5FFFFF address space. My pre-existing game ROM file is 32MByte in size (clearly larger than the SNES address space) and segment A is to be patched into an existing ROM file at file offset address $01A00000 to $01BFFFFF and segment B is patched into the ROM file at file offset address $01E00000 to $01FFFFFF.

Code: [Select]
#org $400000
#offset $1A00000

  ; ... code for one bank here


#org $400000
#offset $1E00000

  ; ... code for another bank here

jonk

  • Sr. Member
  • ****
  • Posts: 273
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #16 on: May 17, 2016, 10:19:38 pm »
Code: [Select]
#org $400000
#offset $1A00000

  ; ... code for one bank here
#org $400000
#offset $1E00000

  ; ... code for another bank here

Cool. Got it. That works for me. Somehow I'd missed the #offset stuff. I saw you use the word, but hadn't seen it in a context. Now I do. Thanks!



May 17, 2016, 10:24:56 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
They can't ... it's up to the programmer to tell the assembler what addressing mode to use based upon the programmer's knowledge of their own code and where the different linked-segments are going to reside.
It's trivial to let the programmer keep the the assembler up to date on this. It's also done all the time on the x86 and segment registers aren't changed all that often there, either. The idea helps to avoid programmer counting errors, which they shouldn't be dealing with anyway.

We'll just have to disagree here. I think an assembler should support this. Period. The fact they didn't in the olden days is no excuse. (The assembler can default to "ASSUME DP = NOTHING" for those who don't want to know or care about it.)
« Last Edit: May 17, 2016, 10:32:21 pm by jonk »
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #17 on: May 17, 2016, 11:02:26 pm »
I agree that the assembler should be smart about instruction size.  Having to manually select direct page/etc for each instruction is unnecessary.  The question now is how can the assembler (or more specifically, the linker) determine the appropriate size?  This was a simpler task when I had a one-part assembler with no linker.  Now that I have to worry about symbol exporting/importing it gets more complicated.


Most constants and variables are going to be able to be evaluated easily enough, but label names get tricky, since you need to know the size of all instructions between the label and the previous ORG to properly evaluate it -- and the size of those variables may depend on evaluation of that label!

Code: [Select]
lda foo, X
nop
nop
rts

foo:
 #byte 0, 1, 2, 3

Can't know the size of that lda until I evaluate foo, and can't evaluate foo until I know the size of that lda.


My first thought is to track a running min and max size so labels can immediately be given a possible range that they fall within.  From there, you might be able to "rule out" DP/Absolute mode based on the given range.  It wouldn't be full-proof, but I'd imagine it would work for most use cases -- unless you are intentionally trying to trip up the assembler.

But that introduces a set of problems with macros and conditional assembly.  Barf.


I'll have to think about it more and post tomorrow.  If you have ideas on how to approach I'm all ears.

elmer

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #18 on: May 17, 2016, 11:39:12 pm »
It's trivial to let the programmer keep the the assembler up to date on this. It's also done all the time on the x86 and segment registers aren't changed all that often there, either. The idea helps to avoid programmer counting errors, which they shouldn't be dealing with anyway.

We'll just have to disagree here. I think an assembler should support this. Period. The fact they didn't in the olden days is no excuse. (The assembler can default to "ASSUME DP = NOTHING" for those who don't want to know or care about it.)

I'm not disagreeing with you. As I said in my post ... the way that the assembler worked is/was to use the appropriate instruction if it knows the size/location of the label when it is assembling it.

And IIRC, "yes", SNASM and other old assemblers had an ASSUME directive to tell it where the DP currently was.

As you say ... that concept came about with the 8086, which was old technology by the time that WDC created the 65816.

I just looked at some old source code, and the "export" directive (a declaration) allowed you to specify attributes for a label ... such as "far", that would tell the assembler how to treat a specific label.

Where you'll really expand upon old practice and do something new, is if you implement LTCG so that you don't need to decide the size of an instruction until link time, rather than at assembly time the way that the old assemblers did it (because of limited CPU power, memory, disk space).

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: 65816: Direct Page vs Absolute Operand
« Reply #19 on: May 17, 2016, 11:42:53 pm »
Where you'll really expand upon old practice and do something new, is if you implement LTCG so that you don't need to decide the size of an instruction until link time, rather than at assembly time the way that the old assemblers did it (because of limited CPU power, memory, disk space).

This is the plan.



EDIT:

The more I think of cases that need to be addressed, the more I remember why I abandoned the idea of a separate linker in my original assembler.  Most of the trouble is coming from conditional assembly.

Consider this basic macro definition to push AXY to the stack:
Code: [Select]
#macro pushregs
  #if in_65816_mode
    pha
    phx
    phy
  #else
    pha
    txa
    pha
    tya
    pha
  #endif
#endmacro

Since phx/phy are not available on 6502, you need to do the tax/pha combo to push X.  And if in 6502 mode, 'phx' would be an unrecognized symbol and would generate an error at compile time.  BUT you would not want this macro to produce an error because that phx is only parsed if in 65816 mode.

For this to work properly, the '#if' condition needs to be able to be evaluated immediately on first pass, so the inappropriate path can be completely ignored.  Which for the above example is trivial, but for other examples it might not be:

Code: [Select]
TimedLoop:
    lda foo, X
    sta bar
    nop
    inx
    bne TimedLoop

#if (current_pc & $FF00) != (TimedLoop & $FF00)
  #error "TimedLoop branch crossed page boundary.  Timing will be incorrect"
#endif

Here, you can't know the current PC until 'foo' and 'bar' are resolved, and the size of those lda/sta instructions are determined.  Which means the #if condition cannot be resolved at compile time.... and certainly not on first pass.



Any ideas how this problem can be addressed?
« Last Edit: May 18, 2016, 11:52:39 am by Disch »