[last edit 12/28 @ 23:30 ish]
BigDumbDinosaur wrote:
magetoo wrote:
This whole discussion makes me wonder whether it would be useful to write in a slightly higher level of abstraction...Then again, I suppose that might be just reinventing another language that already exists.
Assembly language is used to get close to the bare metal and produce a program that executes as rapidly as possible. Hence the notion of using abstraction in assembly language programming seems to be counter-intuitive.
I see
huge value in making something slightly higher than assembler, something that is essentially a full-featured assembler with some convenience features. It lets you get as close to bare metal as you want, BUT you can choose to leave a few things to the translator that either it can do a better job of, or that are tedious, as long as it produces the correct behavior. Something beyond a macro assembler. (A macro assembler doesn't really do this, because the programmer is still in complete control of the code produced, and can know in advance what he's producing as long as he understands macro expansion rules and cares to think it through.)
The key is knowing where it works to let the translator do its magic, and where it won't, and having an adequate way to supply hints that keep the thing from going crazy on you, and transparency into what it's doing.
I have been toying with the idea of an optimizer. I can't tell you how many times I've tried to rearrange or refactor assembly code, or borrow a routine from another project, only to discover through a lot of debugging that there was some optimization I missed that is not valid in the new location.
So basically, I want to be able to write code that may be non-optimal as written, but is straightforward, portable, and reusable. It's only after putting all the subroutines together that you can know all of the optimizations that will actually work. I'm quite OK with letting an optimizer do some of the work for me when I choose to use it. Consider the following two workflows:
- asm source -> (assembler) -> binaries -> (keyhole optimizer) -> optimized binaries
- asm source -> (static analyzer) -> new source w/ suggested optimizations -> (assembler) -> optimized binaries
Now, I think those are valid and useful tool-chains. If those aren't terrible, then why not:
- asm source + hints/constraints/options -> (optimizing assembler) -> BETTER optimized binaries + listfile showing optimizations
Trying to make the front-end or back-end optimizations as efficient as what a translator could do with appropriate hints and constraints is a very tall order, not to mention that an assembler and/or disassembler of some sort must also be present in the optimizer, which is needless duplication and increases development time and complexity.
BigDumbDinosaur wrote:
If high(er) level abstraction is desired then a compiled or interpreted language should be chosen.
It don't think it has to be so black-and-white. There is a huge chasm between high-level languages and assembly. Is there no room for something in between? Asm can be tedious and prone to bugs. Interpreters are very slow, and high-level languages (at least for 6502 architecture) produce slow and very bloated code.
ca65 provides segments, allowing you to keep variables close to the code they refer to, but consolidate all the data somewhere else determined by the linker. Many assemblers also provide alignment options to, for example, keep code or data from crossing page boundaries; they will insert space as needed to stick to those constraints. If I need code to stay within a single page, then the assembler could either pad with NOP statements (no longer a true assembler), or I could do this:
Code:
code, code, code
JMP SKIPSPACE
.alignment option
SKIPSPACE:
aligned code
But, what if only one byte needed to be inserted to get the alignment? Now the JMP instruction could bump the aligned block up
another page, wasting 256 bytes. The assembler could be allowed to insert either 1-2 NOPS or a JMP statement, as appropriate, but again this goes beyond a true assembler. Kick-assembler (I think, from what I've seen) would allow you to write script that makes some such decisions for you, but even though the programmer wrote that script, it is still not obvious until viewing the compiled output what code was generated. So why not let the assembler provide such scripts that are activated using pseudo-ops (hints), rather than making the coder do it? (BTW, I would still like to be able to write my own code-generating scripts as well.)
Also, why not let the assembler locate some of the data in the padding inserted to the code, or rearrange data so that data itself is used to align data structures, rather than blank space. That is clearly something I would do if I were hand-optimizing and needed to save space, but every time I edit some code, I would have to adjust that optimization and move stuff around. Total PITA.
Don't get me wrong... I
love bare metal, and the challenge of coming up with the most efficient code possible. It's better than any puzzle, IMO. I just like the idea of having a tool that lets me do more in less time, some of the time.
BigDumbDinosaur wrote:
As is always the case in software development, automation leads to less efficient code.
If so, optimizers would not exist... but they do.