| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
Kick Assembler - Removal of unused code?
I'm trying out Kick Assembler and I'm wondering if it has a feature I used a lot in my own assemblers over the years.
I like to collect useful subroutines and keep them in an external ASM file that I include at the top of my code. Is there a way that I can do that BUT have the assembler not compile code that I don't actually call?
I hate to eat the memory space for stuff that I won't be calling in that specific app.
How do you guys handle this sort of thing? Am I over engineering? :) |
|
| |
Zirias
Registered: Jan 2014 Posts: 48 |
A feature you used a lot? How would that work? While it's relatively easy to detect definitely "dead code" in C, I don't see how an assembler could reliably do that, e.g. think about self-modification with calculated jump targets...
With ca65, you could take advantage of the linker (ld65): have each of your routines in a separate translation unit, pack them into one static library with ar65. When you link this library, the linker will only include object files with code that is actually referenced. You can have an include-file with this library that has all the ".import" statements necessary -- ca65 discards ".import"s that aren't actually used. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
In my case, I had a special way to designate routines that were optional for compilation. So something like:
.sub MyFunc
{
}
If "jsr MyFunc" never appears in the code, then it excluded that subroutine from the final compilation.
So I guess I could reframe the question to be something like:
How does someone with lots of little utility functions organize their code base? Do you simply copy/paste the stuff you need in?
I thought about some sort of system where every sub routine lived in it's own ASM file but that seems crazy. |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
I guess you can use IF statements with some flags in your init part where set them to 1 when used functions need to be compiled. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
I had thought of that. Seemed clunky so I was hoping maybe Kick had something to automate the process but I guess not.
Thanks for the replies! I'll work around... |
| |
Erol
Registered: Jul 2003 Posts: 6 |
Macros and pseudo-commands in separate .asm file please check http://www.theweb.dk/KickAssembler/webhelp/content/cpt_Function.. if you don't call it via macro or pseudo command it will not get compiled, so only used macros and pseudo-commands are actually compiled and present in memory. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
Thanks! Yes, macros and pseudocommands work the way I want but that doesn't fly if the routine is somewhat large. I don't want to macro it into the code everywhere I call it ... say, for example, a memcopy routine.
In that case, I want to make it a routine that compiles once in memory BUT gets ignored if nobody calls it.
Functions don't really fly because they can't generate byte code. Just the internal scripting commands are allowed. Unless I missed something. |
| |
Zirias
Registered: Jan 2014 Posts: 48 |
Quoting TaskmasterI thought about some sort of system where every sub routine lived in it's own ASM file but that seems crazy.
Why? At least with something like ca65, it isn't -- you can have something "self-contained" (with code, data, bss, zeropage segments) and only link it when needed. To use it, you build a static library from all these files.
When working only at the source level, it's of course a bit more cumbersome. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
I should have been clearer - crazy for ME. :) I'm not that advanced at this point, so doing all of that is sort of beyond my scope at the moment.
But thanks, it's good to know that sort of thing is possible... |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
i know its not what you ask - but 64tass can do what you want, even semi automatically. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
No, that's super helpful, thanks!
.proc
.pend
That looks like what I want. I'll dig into 64tass a little then, maybe that's a better fit for me. |
| |
Digger
Registered: Mar 2005 Posts: 437 |
Interesting problem... I've found this article on Macros (http://wilsonminesco.com/StructureMacros/index.html) but it seems a little bit over the top for me to code like that, code is more readable but longer and slower.
It's possible to do purely with KickAss macros:
// Address for all compiled macros
.var libAddrOffset = $2000
// Address (plus flag) for the compiled macro
.var setColorsUsedAddr = null
// Here comes the proc
.macro setColors() {
// Store current program address
.var currentAddr = *
// Check if macro was used before
.if (setColorsUsedAddr == null) {
// Not yet, compile at lib address
* = libAddrOffset
// Mark the macro as used and store its address
.eval setColorsUsedAddr = libAddrOffset
stx $d020
sty $d021
rts
// Update the library address (for additional macros)
.eval libAddrOffset = *
}
// Restore program address
* = currentAddr
// Call the proc
jsr setColorsUsedAddr
}
// Example: Proc called twice, compiled once
// If not used, won't compile
* = $1000
ldx #2
ldy #4
setColors()
ldx #7
ldy #8
setColors()
jmp *
Here's the result:
(C:$100e) d 1000
.C:1000 A2 02 LDX #$02
.C:1002 A0 04 LDY #$04
.C:1004 20 00 20 JSR $2000
.C:1007 A2 07 LDX #$07
.C:1009 A0 08 LDY #$08
.C:100b 20 00 20 JSR $2000
.C:100e 4C 0E 10 JMP $100E
.C:1011 00 BRK
(C:$1029) d 2000
.C:2000 8E 20 D0 STX $D020
.C:2003 8C 21 D0 STY $D021
.C:2006 60 RTS
.C:2007 00 BRK
This could be even abstracted out in a more elegant fashion, i.e. store addresses as List() and create a macro called .proc() that would wrap just the macro code. Let me know if you need help with that :) |
| |
Compyx
Registered: Jan 2005 Posts: 631 |
Wow, that's all you need? :) |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
That's really cool! Complex but cool.
I actually understand what's going on there so that's a good sign.
Kick has a lot of power, apparently. Thanks for that, I'll give it some thought. :) |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
I've been trying to build on that awesome example by Digger. I'm stuck on something I'm sure is simple but I don't know enough about Kick scripting yet.
How can I create a string and then drop it into the source code, as if I had typed it? You know, for dynamic labels and things like that?
Take this code...
.macro DeclareIt( _name_ )
{
lda #0
rts
.eval _name_ + "_Var:"
.byte 0
}
Main:
DeclareIt( "MyMacro" )
lda MyMacro_Var
rts
What I want is for "MyMacro_Var:" to become a real label but while I can build the string, I can't drop it into the source for compilation ...
What am I missing? |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
What I'm getting at is, once this expands out and hits the assembler, I want it to look like this:
lda #0
rts
MyMacro_Var:
.byte 0
lda MyMacro_Var
rts
(NOTE : yes, the code is total nonsense, I'm just trying to get a successful compilation at this point)
:) |
| |
Digger
Registered: Mar 2005 Posts: 437 |
Been trying to ask Mads aka Slammer exactly about the same! ;-) |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
Haha, well, please report back if he helps you. :) |
| |
Digger
Registered: Mar 2005 Posts: 437 |
You can target macro vars with dot, i.e. LDA MyMacro._Var AFAIR ;-) Don’t need dynamic labels for that. |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
Right, but my question isn't about that.
I want to dynamically create, say, label names from strings being passed in.
Like ...
DeclareIt( "MyMacro" )
DeclareIt( "MySecondMacro" )
DeclareIt( "MyMacroThird" )
Gives me 3 macros using that base string as the root of their names and variable labels. Essentially, using the scripting language to generate source code from what is effectively a template ...
Does that make sense? |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
Quote: Right, but my question isn't about that.
I want to dynamically create, say, label names from strings being passed in.
Like ...
DeclareIt( "MyMacro" )
DeclareIt( "MySecondMacro" )
DeclareIt( "MyMacroThird" )
Gives me 3 macros using that base string as the root of their names and variable labels. Essentially, using the scripting language to generate source code from what is effectively a template ...
Does that make sense?
describe what your ultimate goal is maybe there is a simpler better another solution to it. |
| |
Frantic
Registered: Mar 2003 Posts: 1648 |
That is already described in the first post? |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
To be fair, we sort of switched topics ... or side stepped, anyway.
But currently what I'm looking to do is use the Kick scripting language, or something else, to generate code.
This would allow me to write some generic wrapper macros that would auto-write the boilerplate necessary for the technique that Digger was sharing. |
| |
Digger
Registered: Mar 2005 Posts: 437 |
Here's slightly "abstracted out" and cleaner version, using some tips from Slammer:
https://pastebin.com/print/cZqgdv7C
What remains is to figure out:
1) the ways of passing params to these lib code, a good write-up here: https://maciejmalecki.github.io/blog/macro-hosted-subroutines
2) what's the best way to utilise macro's variables |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
Wow, this got a lot deeper than I was expecting ... thanks for the replies, everyone, it's been informative! |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
So, to underscore what I wanted to do here in the end ... I wanted some way to boilerplate the code that Digger inspired above.
LONG STORY SHORT, I WANT TO USE THE SCRIPTING LANGUAGE TO GENERATE CODE THAT THEN GETS COMPILED AS IF I HAD TYPED IT.
Code like this gives me what I want, in terms of conditional compilation and I love it:
NOTE : SUBSYS_MasterAddr is defined elsewhere.
.var GFX_HideROMCharSet_Addr = 0
.macro GFX_HideROMCharSet()
{
.if( GFX_HideROMCharSet_Addr == 0 )
{
.var save_pc = *
* = GFX_HideROMCharSet_Addr = SUBSYS_MasterAddr
{
lda #$37
sta $01
cli
rts
}
.eval SUBSYS_MasterAddr = *
* = save_pc
}
jsr GFX_HideROMCharSet_Addr
}
The trouble is, this is all the code that I actually need:
lda #$37
sta $01
cli
All the rest is boilerplate cruft. So I wanted a way, in Kick Assembler, to generically insert that code for me. But I need some sort of text replacement ability before compilation begins. It FEELS like it should be possible. So something like:
ProcStart(GFX_HideROMCharSet)
{
lda #$37
sta $01
cli
}
ProcEnd(GFX_HideROMCharSet)
"ProcStart" and "ProcEnd" take the name I provide it and drop it into the assigned slots in the code templates and away we go. So, everywhere you see a [ARG_HERE] ...
-----------------------
ProcStart
-----------------------
.var [ARG_HERE]_Addr = 0
.macro [ARG_HERE]()
{
.if( [ARG_HERE]_Addr == 0 )
{
.var save_pc = *
* = [ARG_HERE]_Addr = SUBSYS_MasterAddr
{
-----------------------
ProcEnd
-----------------------
rts
}
.eval SUBSYS_MasterAddr = *
* = save_pc
}
jsr [ARG_HERE]_Addr
}
This feels like it SHOULD be scriptable ... is it? |
| |
Digger
Registered: Mar 2005 Posts: 437 |
Check my pastebin code above :) |
| |
Taskmaster Account closed
Registered: Feb 2018 Posts: 22 |
I saw that, thanks!
And that is a way to get where I'm going but I'm now wondering about the more generic question ...
Is there a way to write code with the Kick scripting language so that it dynamically creates source that will be compiled in later passes?
I know you can use FOR loops with VARs and modify operand arguments, but ... is there any way to emit straight text or strings?
Like, if I have:
.var myCmd = "lda #0"
Can I get that "lda #0" line emitted to the source code somehow? |
| |
Digger
Registered: Mar 2005 Posts: 437 |
Why would you like to do it?
I often use JS to generate the KickAss code that later gets compiled, but that's useful usually for speed code, for example for texture lookups. |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
* = $1000
changebackr .proc
sta $d020
rts
.pend
jmp *
result in memory:
.C:1000 4C 00 10 JMP $1000
* = $1000
changebackr .proc
sta $d020
rts
.pend
jsr changebackr
jmp *
result in memory:
.C:1000 8D 20 D0 STA $D020
.C:1003 60 RTS
.C:1004 20 00 10 JSR .changebackr
.C:1007 4C 07 10 JMP $1007
also macros do what you want in your post #1, they only get compiled into memory when you use them.
* = $1000
changebackgr .macro
lda #\1
sta $d020
.endm
#changebackgr 0
jmp *
result in memory:
.C:1000 A9 00 LDA #$00
.C:1002 8D 20 D0 STA $D020
.C:1005 4C 05 10 JMP $1005 |
| |
Compyx
Registered: Jan 2005 Posts: 631 |
That looks like 64tass code. The topic is about Kickassembler. |