6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 11:23 pm

All times are UTC




Post new topic Reply to topic  [ 43 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: A sensible macro engine
PostPosted: Sat Jul 13, 2013 5:15 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
Having exchanged a few posts regarding how macros should be handled, I can't stop thinking about it. I think it's worth a separate topic.

It seems that every assembler I've worked with got some part of it wrong (and I've sifted through dozens of assemblers, tossing them out as unusable due to some or other misimplementation). I am sure there were reasons for the decisions made, but I can't help thinking that at least some of them were made to workaround problems created by other poorly thought out decisions. Having written assemblers and compilers in the past, I personally have made some poor choices simply because I failed to understand the problem at hand and was invested in code that should have been re-written.

So I will propose a logical framework for working with macros. If you can think of something you do with an assembler that my method cannot handle please let me know. I would prefer to avoid a religious fight over syntax or 'that's not how assemblers do it'. I also do not mean to suggest that my way is the 'right' way. I just want to explore the simplest possible way of doing macros that makes sense.

I am going to do my experiments in python as it has adequate facilities for memory management and expressive power (I want to avoid the usual memory bugs and focus on higher-level functionality). My experience with python is limited to about 3 hours, so don't expect much. Although I generally avoid things like that, I find that python is pretty easy to understand. Python is also interactive, and allows execution of strings, which makes it very flexible as an extension language.

Perhaps the macro subsystem can be made flexible enough to implement the assembler in.

Summary

There exists a memory space initialized with the file being assembled. Macro expansion proceeds in one pass, with characters parsed from SRC, the source pointer. As words are parsed (according to parsing rules), they are inserted at SRC, unless they match a name in the macro dictionary. A macro match triggers an expansion of the macro into SRC, following parameter substitution rules. The new text will be processed as any other text. Only one pass is required. Note: the implementation may work differently, but conceptually equivalent to a memory space where expanded macro contents get inserted ahead of SRC.

Parsing rules

Identifiers are terminated with a whitespace or a special character ',' ';' [I will update this as needed]

Parameter rules

This is a little tricky. For assemblers, a comma-separated list makes sense, although what terminates it? <CR>, ';' perhaps. There should be a default value, and the possibility of skipping, as well as a variable-length list. I think named parameters are a bad idea and no assembler I've used implements it.

Error handling

The assembler will get enough information to generate error messages (as a stack of context records containing filename, line number and position within the line, at least).
EDIT: I forgot about my idea of appending the state information (file/line number/position) to the end of the comment on each line.... Although Kingswood assembler chokes without writing any of the listing, so it would not help anyhow.


Listing support

I think the macro engine should remove the comments for the assembler. The listing file can be generated by using the original source line (inserting the data fields necessary). Depending on settings, macros can be expanded into the listing as well. This requires a little more thought to get it right.

Intrinsic support

Include and other built-in keywords need to coordinate with the macro engine. Inclusion is handled as any other macro expansion.

Symbol handling

Since we are dealing with macros only, everything is text. A macro definition is stored in a Forth-like dictionary, with later definitions overloading older ones with the same name. An incomplete macro definition is not visible. A definition within a definition is handled simply: the inner definition is treated as any other text and no new macros are defined. When the outer macro is expanded, the newly-expanded text will contain the inner macro definition, and the macro will be created when that text is processed.

I think it would be useful to have a 'forget' command, perhaps? Maybe not needed.

Ideally the macro system should be completely assembler-agnostic and capable of anything (is it possible to keep the macro defining and ending words flexible, so a macro can be ended with an 'endm' or 'end macro' or '}'?). If that is not possible, I would settle on a sensible syntax that is usable.


Should the macro engine be aware of anything beyond text expansion? Should it know about lines, or fields within a line? I hope not.

I think that's enough to do just about anything. If I missed anything or if you have syntax ideas please let me know. Please remember that the point of this exercise is to keep it simple and to avoid special cases, but sufficient to do anything that we may need to do with macros.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 13, 2013 9:29 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 672
Quote:
An incomplete macro definition is not visible.
Take care to ensure that recursive macros are still possible. It sounds like it might be, but it should be an explicit requirement.

Quote:
Should the macro engine be aware of anything beyond text expansion? Should it know about lines, or fields within a line? I hope not.
You need to ensure that at least line numbers will be retained throughout the pipeline for good error reporting. This means that the output of a macro system is not just expanded text, but requires annotations somehow to note what file & line number the bits came from.


If you really look at it, every assembler's macro & state system really wants to be a full programming language, managing assemble-time variables for anything from building labels & tables to full on code generation. Being able to generate code from code is the all-encompassing ideal; any deviation from that is making hopeful tradeoffs. If you're serious about a macro system that will not be constrained, and you're looking at starting to use different tools anyway, Lisp is king in that field. Python borrows a fair amount from Lisp, so it shouldn't be too foreign.

While there are very few tools around for general Lisp-based assemblers, here's a snippet I found via google: link
Quote:
In 1983, Rescue on Fractalus and Ballblazer, the first releases from Lucasfilm Games, were built with a system written in Lisp. It was a 6502 assembler with Lisp syntax and a Lisp-style macro facility, written in Portable Standard Lisp, running in Berkeley Unix on a 4MB VAX 11/750. Unfortunately it was eventually abandoned because the developer had left and it was a bit of a strain on the VAX that was shared by the whole development team.

Yes, I wrote it. Yes, it was my first non-academic programming job. Yes, the users complained about the parentheses, and the slowness. But, they also took advantage of the powerful macro facility.
(Of course, Lisp implementations are generally native-speed compilers nowadays.)

This page is quite technical, but writes a macro assembler in Lisp targeting the NES. While some of the main code looks like fairly straight 6502 code, the music for the demo ends up looking like this:
Code:
(four-on-the-floor ()
  (repeat 4 (thump 32 (et -24))))

(intro-beat ()
  (measure
    (four-on-the-floor)
    (seq
      (swagger)
      (swagger))))

(intro-fill-1 ()
  (measure
    (four-on-the-floor)
    (seq (swagger)
         (stagger))))

(intro-fill-2 ()
  (measure
    (four-on-the-floor)
    (seq (jagger)
         (jagger))))

Finally, I've built up to defining the music. Here, the para (parallel) operator combines its two inputs: a four measure drum pattern, constructed in an AAAB pattern from intro-beat and intro-fill-1, and a sequence of four arpeggiated chords, each one measure long. This defines the first four measures of the music:

(para
  (phrase-aaab
    (intro-beat)
    (intro-fill-1))
  (seq
    (measure (fat-arp 128 '(0.00   0  3  7 11) :rate 4 :volume (volramp  8 -1/22)))
    (measure (fat-arp 128 '(0.00   0  2  5  8) :rate 4 :volume (volramp  8 -1/22)))
    (measure (fat-arp 128 '(0.00  -2  7  8 12) :rate 4 :volume (volramp  9 -1/20)))
    (measure (fat-arp 128 '(0.00  -1  2  3  7) :rate 4 :volume (volramp 10 -1/18)))))

That's basic macro expansion that does all the right thing and has full programming language power inside the macro expanders. It's much higher level than you'd get from other macro options, and is still straight assembly instruction & byte-level definitions under user control.


tl;dr: Write a 6502 assembler as a DSL inside an existing programming environment that already does all the cool & solid macro stuff.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 13, 2013 9:52 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
White Flame wrote:
Take care to ensure that recursive macros are still possible. It sounds like it might be, but it should be an explicit requirement.
It absolutely a requirement. However, there is a limitation: there must already be a macro by that name; otherwise the expression is nonsensical. Example:
Code:
MACRO test
  lda #5
ENDM
....
MACRO test
  test
  rts
ENDM
...
test

expands to:

  lda #5
  rts

Without the initial definition, there is no macro named test and macro expansion fails. Since the macro definition has not been closed, there is no sensible way to expand the macro.

Quote:
You need to ensure that at least line numbers will be retained throughout the pipeline for good error reporting. This means that the output of a macro system is not just expanded text, but requires annotations somehow to note what file & line number the bits came from.

Yes, exactly what I was thinking. The more I think about it, the less of an issue the assembler itself is - writing an assembler is tedious but not hard if the macros are taken care of.

Quote:
If you really look at it, every assembler's macro & state system really wants to be a full programming language, managing assemble-time variables for anything from building labels & tables to full on code generation. Being able to generate code from code is the all-encompassing ideal; any deviation from that is making hopeful tradeoffs. If you're serious about a macro system that will not be constrained, and you're looking at starting to use different tools anyway, Lisp is king in that field. Python borrows a fair amount from Lisp, so it shouldn't be too foreign.

While beautifully stated, I think I disagree just a little with the first sentence. I think it's possible to look at the macro subsystem strictly as a text transformation engine, completely unaware of the language. While it may make sense to comingle symbol tables and state information for the sake of error reporting or overall integration, the macro system should really be independent.

I have deep respect for LISP from a distance, having never delved into it. It does look very tempting at times like these...

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 13, 2013 10:08 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
enso wrote:
It seems that every assembler I've worked with got some part of it wrong (and I've sifted through dozens of assemblers, tossing them out as unusable due to some or other misimplementation). I am sure there were reasons for the decisions made, but I can't help thinking that at least some of them were made to workaround problems created by other poorly thought out decisions. Having written assemblers and compilers in the past, I personally have made some poor choices simply because I failed to understand the problem at hand and was invested in code that should have been re-written.

So I will propose a logical framework for working with macros. If you can think of something you do with an assembler that my method cannot handle please let me know. I would prefer to avoid a religious fight over syntax or 'that's not how assemblers do it'. I also do not mean to suggest that my way is the 'right' way. I just want to explore the simplest possible way of doing macros that makes sense.

The problems mostly seem to come from the supplier having a narrow idea of what the end user will want, and not making the software flexible to accommodate future ideas or even things in current usage that he wasn't aware of.

  • I like the cheap, old DOS-based CAD I use because it's simple and doesn't try to second-guess me and tell me I can't do this or that when it doesn't understand some of the unorthodox things we do. I've done plenty with it that the supplier never dreamed of in the early 90's when they wrote it. OTOH, I used OrCAD for a while which was 5-10 times as expensive and presented bugs and frustration at every turn.

  • One thing the company I work for makes is high-end intercoms for private aircraft. Some aircraft owners have unorthodox ideas of a custom installation so they call me to ask how or if something can be done; and just when you think you've seen everything but a straight banana, along comes someone else with another off-the-wall idea, and when you hear them out, you usually find they have a good reason for it. Fortunately I can usually give them a way to do it, unlike the situation with a lot of software we encounter.

  • My Hewlett-Packard hand-held computers (HP-41 & -71) are an excellent example of systems whose original designs did not paint them into corners. The result is that they could accommodate a lot of new module and peripheral types and third-party software add-ons that were not envisioned at the time of the original design.

  • Actually, that's part of why I like Forth too. :wink:


Quote:
Parameter rules

This is a little tricky. For assemblers, a comma-separated list makes sense, although what terminates it? <CR>, ';' perhaps. There should be a default value, and the possibility of skipping, as well as a variable-length list. I think named parameters are a bad idea and no assembler I've used implements it.

The bug in the C32 assembler for the parameter list is that if it sees a comma in a quoted string, it takes it to be a parameter delimiter even though it has not reached the ending quote mark yet. Because of this, I had to forgo the use of the HEADER macro when I wrote my '816 Forth and came to words with commas in them. A semicolon should be accepted if it's in a quoted string too, but otherwise would mean that the rest of the line is comments to be ignored.

One thing I really liked about the 2500AD assembler was that it allowed different numbers of macro parameters, and then inside the macro, you could say basically, "If there's a fourth parameter, do this...If there's a fifth parameter, do this..." It's very useful sometimes. In C32 you just have to pad unused parameter spaces with zeroes or something else, which does not contribute to the goal of making it more clear what you're doing.

I'm not sure what you mean about named parameters, but it is convenient to have the macro definition use local names for the parameters instead of only referring to them as parameter #1, parameter #2, etc.. OTOH, if you can do both, for example if it's just an unknown-length list of flag variables to clear, so much the better.


Quote:
Listing support

I think the macro engine should remove the comments for the assembler. The listing file can be generated by using the original source line (inserting the data fields necessary). Depending on settings, macros can be expanded into the listing as well. This requires a little more thought to get it right.

It's helpful to have the list file show the macro expansion when you're still checking a macro. I did this a lot when writing the program-structure macros, to make sure branches were done to the right places and under the right conditions. Usually macro expansion can be turned on and off in the listing with directives that can be used as many times as you want in the source code.

Quote:
Symbol handling

Since we are dealing with macros only, everything is text. A macro definition is stored in a Forth-like dictionary, with later definitions overloading older ones with the same name. An incomplete macro definition is not visible. A definition within a definition is handled simply: the inner definition is treated as any other text and no new macros are defined. When the outer macro is expanded, the newly-expanded text will contain the inner macro definition, and the macro will be created when that text is processed.

I recently made the mistake of forgetting the ENDM directive at the end of a macro, then copied it a few times for similar macros, and of course ended up with problems. It didn't take long to figure out what I did. However, IIRC, what happened was that when the assembler came to a MACRO directive without first finding an ENDM for the last one, it just scrapped everything since the last MACRO directive.

Quote:
I think it would be useful to have a 'forget' command, perhaps? Maybe not needed.

FORGET in Forth backs up the dictionary pointer so the word you FORGET, plus everything after it, is gone. It's far less drastic than COLD (or reset), but there's still probably no need for that. I am not aware of any use for it in an assembler, and PCs' memory is not exactly in short supply these days. If new definitions override (and possibly use) old ones with the same names—a need which I'm sure is rare—I think that would accomplish the purpose.

Quote:
Ideally the macro system should be completely assembler-agnostic and capable of anything (is it possible to keep the macro defining and ending words flexible, so a macro can be ended with an 'endm' or 'end macro' or '}'?). If that is not possible, I would settle on a sensible syntax that is usable.

Flexibility is always good as long as it doesn't create other problems, like that it could see "END MACRO" and stop reading after the "END" (which C32 would do).

Quote:
Should the macro engine be aware of anything beyond text expansion? Should it know about lines, or fields within a line? I hope not.

Fields? Not as in column numbers. Lines? What are you referring to? I do like the fact that in most Forth assemblers, you can put more than one assembly-language instruction on a line so you can group them logically and make the code take fewer lines. Normal assemblers can't do that.

Quote:
I think that's enough to do just about anything. If I missed anything or if you have syntax ideas please let me know. Please remember that the point of this exercise is to keep it simple and to avoid special cases, but sufficient to do anything that we may need to do with macros.

If you want lots of people to adopt your creation, it would be good to run it by them during the process, as someone will come along with that off-the-wall but great idea that requires a change in course before it's too late to do it gracefully.

White Flame wrote:
Quote:
An incomplete macro definition is not visible.

Take care to ensure that recursive macros are still possible. It sounds like it might be, but it should be an explicit requirement.

Something like a smudge bit in a Forth header? (RECURSE still allows recursion though, without doctoring up the smudge bit.)

And, to reiterate, I would like the ability for macros (if not the whole assembler) to have assembler variable arrays and the possibility to index into them, if not the direct ability to make stacks and to have stack-relative addressing into them and even to know their depth, for the purpose of forming and nesting program structures. The fact that they don't is why I didn't come up with my program structures many years earlier, and why it took a rather mickey-mouse work-around to make the concept work. There are still more structure macros I would like to do if it weren't so cumbersome in C32, the otherwise very good assembler I use on the PC.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 13, 2013 10:41 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
GARTHWILSON wrote:
The bug in the C32 assembler for the parameter list is that if it sees a comma in a quoted string, it takes it to be a parameter delimiter even though it has not reached the ending quote mark yet.

Good point, it has to be aware of strings and perhaps inline comments and skip them.
Quote:
One thing I really liked about the 2500AD assembler was that it allowed different numbers of macro parameters...

Yes, this is an issue. Definitions should be allowed to set default values, and I am inclined to use python-like 'None' to indicate that no parameter is being sent.

By named parameters I mean verilog-style named parameters. You can specify out-of-order parameters by name given in the macro definition. This makes the code long-winded, error-prone and annoying when someone else names parameters. Naming parameters inside the definition is for another day...

Arrays and fields should be handled by the assembler itself, in the expression parser.

Thanks for the comments.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 4:58 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 672
enso wrote:
White Flame wrote:
Take care to ensure that recursive macros are still possible. It sounds like it might be, but it should be an explicit requirement.
It absolutely a requirement. However, there is a limitation: there must already be a macro by that name; otherwise the expression is nonsensical. Example:

That's not a recursive macro. That's redefining a macro, just with the old one still in scope. If a macro body contains any sort of conditional, then a truly recursive macro will be able to terminate finitely. However, that means the conditional is evaluated as part of the macro system. This is why it's hard to separate out a macro system from a programming environment; the things that make the decision on what to include or exclude need to be informed and capable.


GARTHWILSON wrote:
One thing I really liked about the 2500AD assembler was that it allowed different numbers of macro parameters, and then inside the macro, you could say basically, "If there's a fourth parameter, do this...If there's a fifth parameter, do this..." It's very useful sometimes. In C32 you just have to pad unused parameter spaces with zeroes or something else, which does not contribute to the goal of making it more clear what you're doing.

ca65 has some really good support for that, too. But what if you want to say "Expand this for each of the remaining macro parameters"? Again, the context of the macro invocation is something that contains state and invites making decision on that state, in order to determine what to expand. But I do think you are already in agreement with "macro language = capable programming language", based on your last paragraph.


enso wrote:
Arrays and fields should be handled by the assembler itself, in the expression parser.

The reason for the macro system having this is so that separate macro invocations can be aware of what's going on with each other. It's really the only way to have, for instance, a set of FOR/NEXT structure macros, because they need some storage to remember what was where in the source code, and how they nest. What do you think about the inability to have such macros? Is that within the scope of your tradeoffs in defining a simple but sensible macro system, or is that a capability that you'd like to have in there?

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 6:24 am 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
As I see it, it is not really sensible for a macro to invoke itself inside its own definition. That would require a multipass 'magic' that is confusing. Maybe I am wrong, but what could it mean to invoke a process that has not yet been described? The only sensible meaning I can think of is that the macro is invoking another macro with the same name, hopefully defined previously. FASM works that way and I use this feature all the time to define macros that contain the previous definition along with some new items.

I still think that arrays and structures should belong to the assembler, not the macro engine.

You do have a point about FOR/NEXT and other complex macros/keywords. It appears that there has to be a general purpose processing component to allow macros to be smart. Perhaps Python should be used for that. I have to think about how to do that before embarrassing myself even more...

Thank you for pointing out these things to me!

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 7:38 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
White Flame, or anyone, please could you give a useful example showing why a recursive macro would be worth having?
Thanks
Ed


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 8:03 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
enso wrote:
As I see it, it is not really sensible for a macro to invoke itself inside its own definition. That would require a multipass 'magic' that is confusing. Maybe I am wrong, but what could it mean to invoke a process that has not yet been described?

I think the idea is not that the macro gets invoked during the process of its own definition, but rather that after it is defined, invoking the macro could result in futher invocations of the same macro, assembling more code, until the macro-exit conditions are met. I haven't done it myself, but certainly there are recursive program components of various kinds in the fields of programming. I recently saw an example that made perfect sense. Now if I could just remember where it was...

Quote:
I still think that arrays and structures should belong to the assembler, not the macro engine.

Just so the macro engine doesn't shut down between macros. Having a stack that remains intact after a macro is finished running is really the only practical way to take care of nested structures and certain things we can do with the macros, as certain macros need to communicate info to other macros without clobbering the info needed by still other ones in the nesting. In some cases, the number of stack cells needed will even be variable, and the top stack cell tells the macro how many below it to handle.

I have some things in Forth I would like to pull off in assembly too but the clumsiness of my jury-rigged assembler stack kind of prevents them. CASE in particular is not implemented the ideal way in my macros for this reason. SET_OF (for use along with CASE_OF and the less-common RANGE_OF in a CASE structure) is practically off limits for the same reason. I also forwent (forgoed?) the equivalent of Forth's "compiler security" in the macros because it further complicated stack usage. Compiler security makes sure you don't have mismatched structure words like NEXT without a FOR before it, IF...NEXT without a FOR or END_IF in between, etc., or have overlapping structures like IF...FOR...END_IF...NEXT. Since assembly only allows one instruction on a line, hopefully good indentation practice would make these get caught visually.

Another thought just came to mind—If the macro engine is kind of a pre-processor, would it know what address it's at at any given time if it's not dealing with the other code between macros? It would definitely need to for the structure macros. Would it run its second time between the first and second passes of the rest of the assembler? You may be on to something I just don't see yet. I've been at the other side of the table before, so I don't want to automatically discount it.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 9:37 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Another feature one might accept or reject as important: forward references. I think enso has said that a macro must be defined before it is used. Is that a restriction anyone finds important?
Cheers
Ed


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 14, 2013 3:22 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
Forward references for regular symbols, such as calls to procedures not yet defined, are pretty much expected for an assembler. They are not technically necessary (we can write code bottom up, as in Forth) but nice. Forward references for fixed-size symbols can be resolved in a single pass using 'fixups'.

Forward references for macros are probably not wise as it's impossible to tell what will happen, and will require multiple passes (which I am trying to avoid). On the other hand if any kind of forward references are allowed, it would be consistent to allow macro forward references.

I may be wrong about the macro engine being text only and unaware of the underlying semantics. For instance, it may be good to do conditional assembly based on the location or value of symbols from the assembler domain. But that creates a paradox unless multiple passes are done and the macro expansion is just a part of the assembler pass. In this case I would need to keep track of constants and fully resolved symbols which may be used for macros, and if a macro expansion is required and relies on unresolved symbols, an error results.

My initial attraction to a single-pass macro pre-processor may just be not possible to implement for a usable system. On the other hand, if I strictly prohibit forward references it may still be as usable as Forth (which is pretty usable).

FASM will do as many passes as necessary (I've seen it do over 10) to resolve all issues. It is not very sensible - for instance 'eq' and '=' assignments are done at different times, handling of constants in expressions is almost reasonable but sometimes weird, and macro processing is usable but sometimes _really_ strange.

The whole thing, as I suspected, is a snakepit. The more I think about the less likely that a 'sensible' solution exists. In fact I am leaning more toward Forth the more I think about it.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 15, 2013 11:53 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 672
Regarding recursion, it's often used in place of loops for handling varying numbers of parameters, or managing numbers in table generation until they reach some conclusion. Recursion tends to have more control over what happens to each variable in each iteration than regular loops and can be much more organized, but it's usually technically equivalent to the imperative loop form.

Enso: I would avoid thinking about a fixed number of passes, and just structure it to let it work until it's done. That usually involves running more passes until no changes happen like you mention, or less often having a set of dependencies/constraints/tasks to finish. Fixed passes are a bit conceptually easier in terms of writing the structure of the program, but lock in hard limits when the users start to really push it.

GARTHWILSON wrote:
Another thought just came to mind-- If the macro engine is kind of a pre-processor, would it know what address it's at at any given time if it's not dealing with the other code between macros? It would definitely need to for the structure macros.

It wouldn't necessarily need addresses. If it can create new unique labels, inject those into the source, and remember what the names of those labels were on the context stack, it could be completely agnostic to the actual numeric addresses. (unless that's what you meant already)

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Thu Jul 18, 2013 5:03 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 672
I know I've blathered on about a lot of high level stuff, but I just realized that I didn't describe it in the simple way I normally do:

Macros are assembly-time function calls, whose return value is source code.

Now, most macro systems have lots of syntactic sugar to allow you to write the literal source code that it should return, with some injected functionality. But you could imagine that, as a function call, it could really do anything to build the source code it's going to return, including managing internal state and building up source code from textual strings or AST pieces. Assembly time is the "runtime" of the build system, and that runtime has decisions to make & state to track just like the final assembled program runtime.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Thu Jul 18, 2013 9:42 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
White Flame wrote:
I know I've blathered on about a lot of high level stuff, but I just realized that I didn't describe it in the simple way I normally do:

Macros are assembly-time function calls, whose return value is source code.

Now, most macro systems have lots of syntactic sugar to allow you to write the literal source code that it should return, with some injected functionality. But you could imagine that, as a function call, it could really do anything to build the source code it's going to return, including managing internal state and building up source code from textual strings or AST pieces. Assembly time is the "runtime" of the build system, and that runtime has decisions to make & state to track just like the final assembled program runtime.


I don't think either functions or macros ought to be managing internal state.
That's too "meta"
Frankly I don't want an assembler to be a dynamic language.
If you want that you ought to go to something higher level
and not mess up a perfectly good assembler :wink:


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 19, 2013 3:31 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
bogax wrote:
I don't think either functions or macros ought to be managing internal state.
That's too "meta"
Frankly I don't want an assembler to be a dynamic language.
If you want that you ought to go to something higher level
and not mess up a perfectly good assembler :wink:


But obviously this is what happens, to a point.

Many macros rely upon and set internal flags. Reseting label values, for example.

Having simple symbol substitution and basic math are pretty handy, and can go a long way.

But I agree, you don't need something really sophisticated for the vast majority of use cases.


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

All times are UTC


Who is online

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