Page 1 of 2
Avoiding Circular Dependencies CC65
Posted: Tue May 09, 2023 3:13 am
by Manannan
Hello,
I am trying to remove a circular dependencies from the 6502 assembly in my interpreter but I am not having very much success.
I have reduced my code down to the essential components to demonstrate the issue.
There is a jump from commandLoop.s to a jump table in logicCommands.s, and the another jmp back again. There is an exports.s file so that certain assembly labels are exported for visibility to C code (not shown).
When I attempt to compile this code I receive a: ld65: Error: Duplicate external identifier: 'returnFromOpCodeFalse'
Shifting exports around can make the error go away, but can cause a 'same variable name two different addresses' issue.
I hope my understanding that the global keyword serves as both an export and import command is correct.
I expect there is a dynamic that I don't quite understand.
So in summary I need to know what is the best way to share labels in both directions between two assembly files, and export some of those labels so as to be visible by C.
Here is the code.
commandLoop.s:
Code: Select all
.ifndef COMMAND_LOOP_INC
COMMAND_LOOP_INC = 1
.include "logicCommands.s"
.global returnFromOpCodeFalse
_commandLoop:
ldx #$0
jmp (jmpTable,x)
returnFromOpCodeFalse:
.endif
logicCommands.s:
Code: Select all
.ifndef LOGICCOMMANDS_INC
LOGICCOMMANDS_INC = 1
.global returnFromOpCodeFalse
jmpTable:
.addr b1GreaterThan
b1GreaterThan:
jmp returnFromOpCodeFalse
.endif
exports.s:
Code: Select all
.include "commandLoop.s"
.export _commandLoop
Re: Avoiding Circular Dependencies CC65
Posted: Tue May 09, 2023 4:14 am
by DragWx
Does it work if you replace your
.global directives with
.export in commandLoop.s, and
.import in logicCommands.s? I'd reason that if the linker can't automatically figure out whether a "global" is supposed to be an import or an export, you can fix it by specifying it yourself.
Re: Avoiding Circular Dependencies CC65
Posted: Tue May 09, 2023 7:01 am
by Manannan
Hi DragWx
Good idea; I replaced the '.global' statements with import/export but I got this error:
src/commandLoop.s:8: Error: Symbol 'returnFromOpCodeFalse' is already an import
src/commandLoop.s:13: Error: Symbol 'returnFromOpCodeFalse' is already an import
Re: Avoiding Circular Dependencies CC65
Posted: Tue May 09, 2023 3:46 pm
by DragWx
Oh I see, logicCommands.s is included in commandLoop.s, and I missed that the first time. Hypothetically, you shouldn't need to have a .global/.import/.export for returnFromOpCodeFalse at all.
Edit: .global/.import/.export would only be necessary if you weren't already .including the file.
Re: Avoiding Circular Dependencies CC65
Posted: Wed May 10, 2023 1:10 am
by Manannan
No luck.
Removing all .global statements results 'returnFromOpCodeFalse' is undefined error
Re: Avoiding Circular Dependencies CC65
Posted: Wed May 10, 2023 2:37 am
by DragWx
By any chance, are you passing both commandLoop.s and logicCommands.s to the assembler? If you are, try removing logicCommands.s from the list of files you pass to your assembler/linker; the fact that you .include it into commandLoop.s is enough.
Re: Avoiding Circular Dependencies CC65
Posted: Wed May 10, 2023 5:47 am
by Manannan
Thanks for you help. You were right I was erroneously including the files twice, once as a linker argument and then again as an .include in a file.
I created a single 'main.s' file, which included every .s file within it. Then I changed the make file to include only main.s, as well as any .C files.
This resolved the issue.
I suppose compiling 6502 is similar to C in that you put your C file names as arguments to the linker, but the header files are included in the C files, not added as linker arguments.
I have a follow up question. Why don't the include guards prevent the double inclusion in my case?
Re: Avoiding Circular Dependencies CC65
Posted: Wed May 10, 2023 12:50 pm
by grml
Manannan wrote: ↑Wed May 10, 2023 5:47 am
I have a follow up question. Why don't the include guards prevent the double inclusion in my case?
You're compiling two files, each of them with its own set of includes and macro definitions - they don't carry over from one .s or .c file ("translation unit") to the next. It's also wise to only have declarations in include files, no definitions, so you can actually include it more than once in a project.
Re: Avoiding Circular Dependencies CC65
Posted: Wed May 10, 2023 5:31 pm
by DragWx
Yeah, include-guards are handy for situations where, e.g., you have a source file (main.cpp) that wants to include two different header files (one.h, two.h), but both of those header files themselves include some kind of common header (like a typedefs header). The translation unit for main.cpp would see two #include "typedefs.h" statements: one from #include "one.h" and one from #include "two.h", and if typedefs.h doesn't have an include guard, you'll get errors about duplicate definitions when you try to compile main.cpp.
CA65 provides another way to structure your source code though, if you use .import/.export (or .global). In this case, you'd separately assemble and link all of your .s files like you were doing before, but instead of .including source files into other source files, you'd explicitly tell the linker "this file exports these symbols, and needs to import these symbols from other files", and then it's the linker's job to go through all of the object files (.o) and resolve all of those dependencies. If you had common constants that all files need to know about, you'd still put them all in a common "include" file (.inc extension) and .include it in all interested source files.
It's up to you which method you want to use. The include-all-into-one method is what I'm used to because I come from the DASM assembler which has no linker, and will compile everything with just one translation unit.
The import/export method is more like what C/C++ compilers do and allows your code to compile with multiple translation units (i.e., multithreaded compiling), and then everything is linked together at the end with the linker.
Re: Avoiding Circular Dependencies CC65
Posted: Thu May 11, 2023 3:11 am
by Manannan
grml wrote: ↑Wed May 10, 2023 12:50 pm
It's also wise to only have declarations in include files, no definitions, so you can actually include it more than once in a project.
Thanks for you help. I am trying to completely understand, what you mean by the quote above.
If this were a C question the meaning would be obvious; everyone knows to put the function headers in the .h file and the function definations in the .c file.
However, with assembly language the answer is isn't so obvious; there are no function headers, so what kind of declarations are you talking about?
Do you mean something like, including things 'disp: .word $0', in a .inc file and '.including' that file in .s file.
That would mean that you could pass only '.s' files to the linker, just like you only pass '.c' not '.h' files to the linker in a C project.
Then to jump between code in .s files you could .import/.export the compiler symbols.
Is that what you mean?
Thanks for your help.