Page 1 of 1

Using ACME, how do I work with code in multiple files?

Posted: Tue Sep 14, 2021 6:09 pm
by geon
Hi!

I want to organize my code in multiple files, and have them depend on each other.

Acme has a pseudo opcode for including source: "!src" But when including, you must be careful to JMP past the included instructions/data or include them *after* the main function entry point. But when including files after the code, macros are not accessible to the code above.

Also, having files include other files doesn't work, because the same file can be included multiple times. There is a sort of include guard for this:

Code: Select all

!ifdef FILENAME !eof
FILENAME = 1
But that makes multi pass compilation break: viewtopic.php?f=2&t=5947

I just don't get it. How am I supposed to do it? What are the best practices?

Re: Using ACME, how do I work with code in multiple files?

Posted: Wed Sep 15, 2021 4:45 am
by hmn
These include guards don't work like they would in C, because there is no preprocessor pass, just two assembler passes.

The "Symbol already defined." error in Acme (in the other thread) is caused by the code from the included file not being assembled during the second pass, thus changing the value of the "reset" symbol.

You could just use separate files for macros/definitions (stuff that does not generate bytes during assembly) and code:

Code: Select all

!src "definitions.i"

    * = $8000

start 
    [...]

!src "code.a"

    [...]

Re: Using ACME, how do I work with code in multiple files?

Posted: Wed Sep 15, 2021 7:36 pm
by geon
So, if file A depends on File B and C, and File B and C both depend on file D, What file should include what?

If they contain only macros, I guess they can use include guards and each file can include it's dependencies AT THE TOP, before the code using them.

But if they contain instructions or data, include guards no longer work, so I must use some other method to ensure the file is only seen by the assembler once? Something like a global make-file, that just includes every file in the project? And the instructions/data can be included AFTER the boot loader and main function, or the code must JMP past them?

Basically I'm just wondering how code is organized in a project. I feel like I'm having to invent basic concepts on my own, and I'll get something completely backwards and not even realize how stupid it is.

Re: Using ACME, how do I work with code in multiple files?

Posted: Wed Sep 15, 2021 8:56 pm
by hmn
geon wrote:
So, if file A depends on File B and C, and File B and C both depend on file D, What file should include what?
If you separate code from definitions, that question is easy to answer: You include the definitions wherever you depend on them, and you include the code wherever you want it to be placed in your program.

For definitions you don't strictly need the include guards in Acme (as long as the defined values don't change between passes), but for macros you do.

Re: Using ACME, how do I work with code in multiple files?

Posted: Wed Sep 15, 2021 9:50 pm
by BigDumbDinosaur
hmn wrote:
geon wrote:
So, if file A depends on File B and C, and File B and C both depend on file D, What file should include what?
If you separate code from definitions, that question is easy to answer: You include the definitions wherever you depend on them, and you include the code wherever you want it to be placed in your program.

...and you INCLUDE the definitions before the code that refers to them. Doing it backwards can lead to phase errors.

Re: Using ACME, how do I work with code in multiple files?

Posted: Fri Sep 24, 2021 6:12 am
by cjs
geon wrote:
So, if file A depends on File B and C, and File B and C both depend on file D, What file should include what?
After a bit of experimentation, the approach I finally settled on was to use the include directive only in the top-level file for the application, and directly include dependencies before the things that depend upon them. In other words:

Code: Select all

    include "D"
    include "B"
    include "C"
    include "A"
In some cases the "dependecies" are not actually other files, but just things that need to be defined for a particular platform. For example, my 6800 monitor in `pmon.a68` needs to have `pmon_ramlo`, `pmon_org` and various other things set before the assembler starts assembling it, so I do all that in the top-level file for the particular platform (in my case, currently a simulated machine run in Python and a National/Panasonic JR-200) and then `include "src/mc68/pmon.a68"`.

In either case, I try to ensure that if I forget something some reasonably clear error message is produced. For the pmon thing above, I use AS's `pushv` and `popv` directives (which save and restore symbol values) on them as a no-op that will fail if they're not defined:

Code: Select all

;   Abort assembly with a clear message if an essential definition is missing.
    set ______,
    pushv , pmon_ramlo, rdlinebuf, rdlinebuf_end, pmon_org
    popv  , ______,     ______,    ______,        ______
(That first `set` just ensures that the symbol `______` is defined so that I can restore the values to it, which merely makes for a bit less text for the programmer to read and interpret, and makes it slightly more clear that this is a no-op.)

As well as avoiding the risk of a file being included multiple times, I find that having a top-level file that mentions every other file used in the assembly makes it easier to understand the program as a whole and do optimization on it because I'm never getting stuff dragged in that I might not notice. That's not necessarily an approach I'd be taking with higher-level languages on larger systems, but if I'm writing anything substantial in assembler, I'm almost certainly quite concerned about program size and speed.