Page 3 of 9
New productivity upload: X16 Edit - a text editor
Posted: Fri Oct 01, 2021 5:51 am
by Ed Minchau
On 9/30/2021 at 11:26 PM, Stefan said:
I've done some code cleaning the last couple of days. Very rewarding when the code becomes more compact, more readable, and more stringent. So far I've reduced the binary by almost 200 bytes.
Some time ago, I watched a few lectures on Clean Code by "Uncle Bob". Very inspiring, even though not all paradigms may be used in assembly, if you want things to fly fast.
Guys like Uncle Bob and Kevlin Henney are good to watch if you want your code to be clear and maintainable.
One thing that Bob Martin said in another video which stuck with me is (paraphrasing) "paradigms are just a set of arbitrary restrictions we put on programmers".
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 5:57 am
by Stefan
Continuing on the topic of clean code, even though its not X16 Edit specific, I have some thoughts on how to apply to assembly programming.
In the lesson linked above, Uncle Bob is talking a lot about the design of functions:
Names of functions (and variables) should be carefully chosen, so that reading code is like reading (good?) prose
Function names should contain verbs, as they are doing something
A function should do only one thing; the process of cleaning code entails breaking a function into the smaller and smaller parts, and you know you're done when no more functions can reasonably be extracted
A function should be short (most often 5 lines)
In a modern cross assembler, such as ca65, there's nothing stopping you from naming things properly.
But what about functions doing only one thing, and functions being short like 5 lines?
Any high level language examines functions at compile time, and decides wether the resulting machine code is inlined or made into an actual machine language subroutine. Even if you are writing a lot of really small functions in a high level language, the binary code will probably be efficient.
In 6502 assembly, if you do a lot of subroutine calls with JSR+RTS, they will all stay in the final binary code making it inefficient.
I have never seen 6502 assembly code trying to be clean code in the way Uncle Bob describes. Would it even be possible without loosing performance?
I think it might be, if you use extensive use of macros for code that you want to break out into a separate "function" where the resulting machine code is inlined.
A simple example. Is this a good idea?
.macro goto_topleft
stz VERA_L
stz VERA_M
lda #(2<<4)
sta VERA_H
.endmacro
.macro clear_line
ldx #80
lda #32
: sta VERA_D0
dex
bne :-
.endmacro
.macro goto_nextline
stz VERA_L
inc VERA_M
.endmacro
.macro is_finished
lda VERA_M
cmp #60
.endmacro
.proc clear_screen
goto_topleft
loop:
clear_line
goto_nextline
is_finished
bne loop
rts
.endproc
VERA_L = $9f20
VERA_M = $9f21
VERA_H = $9f22
VERA_D0 = $9f23
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 6:38 am
by kliepatsch
Well, I personally don't like (yet) to extract things that are used only once. It feels messy to me. But the example above makes the code very easy to understand. It took me only a few seconds to understand what you are doing.
I wonder what's better: commenting code (e.g. giving headlines to sections of code) or writing macros with explanatory names
Edit: or even use descriptive names for labels
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 6:39 am
by Scott Robison
I do like using asm macros to abstract away clutter. Something simple like:
LDA #whatever
STA wherever
I think looks much more readable personally behind a STI macro, which I've used recently with Acme on some code that already existed for that assembler:
!macro STI .addr, .value {
LDA #.value
STA .addr
}
It's such a simple thing, but even as tiny as it is, it so much better represents the intent. Now, the name in this case isn't necessarily ideal unless you're trying to keep your asm continuing to look like asm. And for me, if I can take a bunch of these sorts of 2 or 3 or more line "routines", I'm able to have twice as much code on screen in context, which makes comprehension easier (for me).
!macro ADD .dst, .src {
CLC
LDA .dst
ADC .src
STA .dst
}
I like that when it fits the pattern much more than the equivalent four lines. And I can have ADDI, or ADDW, or ADDWI. I think those are good naming conventions for commonly used snippets that can themselves be used to create larger even better named routines.
I like some things about acme. In some ways it is a lot simpler. It's macro facilities aren't as good IMO as ca65, for example, but they are much better than nothing.
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 6:48 am
by Scott Robison
On 10/3/2021 at 12:38 AM, kliepatsch said:
Well, I personally don't like (yet) to extract things that are used only once. It feels messy to me. But the example above makes the code very easy to understand. It took me only a few seconds to understand what you are doing.
I wonder what's better: commenting code (e.g. giving headlines to sections of code) or writing macros with explanatory names
I think the key to programming / software engineering is making that judgement call on a case by case basis. Sometimes one will be preferable to the other.
One thing I haven't done yet, but really need to do, is create alternative mnemonics macros for the various branch instructions.
CMP #value
BEQ label
Is pretty clear. I think it becomes far less clear when it is something like:
AND #mask
BEQ label
To me, reading that, my brain wants to go to the place where "if the relevant bits in A are equal to the mask then branch". But it really is saying the opposite. In a case like this a simple alternative like BZ/BNZ as alternatives to BEQ/BNE makes a world of difference to the way I read the code.
Also, my OCD flares up whenever I see BPL. BMI isn't bad, I can read that as "branch negative". BPL though intuitively (even though I know better) means "branch positive" and zero isn't positive in my mind. I mean, I know how to use it, but the official mnemonics selected for 6502 leave me wanting at times.
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 7:19 am
by Stefan
Bringing clarity to code by lots of comments is not the Clean Code way of doing things, apparently. Comments have the disadvantage of being ignored by the assembler or compiler. Keeping them up to date is a manual process. And all manual processes will fail ?
Good labels are a better option.
The advantage of extracting small functions into macros is that you get better abstraction in higher level functions. In my example above, the function "clear_screen" contains mostly macro calls. It's possible to understand what they will do without looking at the macro. You get the big picture very quickly. And if you are interested in the details, you may look at the macro definition.
That said, I've never tried to program anything in this fashion. It would be interesting to do that.
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 8:12 am
by Scott Robison
Another huge (IMO) benefit if macro based assembly is to avoid copy and paste programming. Say you have a number of functions to load different files. So you have JSR LOAD_FILE_X and JSR LOAD_FILE_Y and each of these are called only once. The code is practically identical in each. Why not wrap it in a macro?
!macro LDVFILE .file, .len, .addr {
LDX #0 ; ram block for file name
LDA #^.addr ; ram block for load address
JSR JSETBNK ; load to bank ^.addr from file named in current bank
LDA #.len ;LENGTH OF FILENAME
LDX #<.file
LDY #>.file
JSR SETNAM ;SETNAM A=FILE NAME LENGTH X/Y=POINTER TO FILENAME
LDA #$02
LDX #$08
LDY #$00
JSR SETLFS ;SETFLS A=LOGICAL NUMBER X=DEVICE NUMBER Y=SECONDARY
LDX #<.addr ;LOW BYTE FOR LOAD ADDRESS
LDY #>.addr ;HIGH BYTE FOR LOAD ADDRESS
LDA #$00
JSR LOAD ;LOAD FILE A=0 FOR LOAD X/Y=LOAD ADDRESS
JSR CLOSE ;CLOSE FILE
}
This could be copied and pasted into multiple routines, but why? It is part of a loader so expanding the macro multiple times isn't harmful in this case, but the actual code becomes much easier to read.
+LDFILE file_a_addr, file_a_len, file_a_addr
+LDFILE file_b_addr, file_b_len, file_b_addr
+LDFILE file_c_addr, file_c_len, file_c_addr
+LDFILE file_d_addr, file_d_len, file_d_addr
+LDFILE file_e_addr, file_e_len, file_e_addr
+LDFILE file_f_addr, file_f_len, file_f_addr
Now, one must be careful. It is easy to get carried away with these and really bloat your code. Hence why the need for judgement and why there isn't a one size fits all approach. But I like looking at the latter example more that expands the macro six times rather than making six copies of the function and tweaking each to use custom parameters.
That doesn't mean I'm perfect and never copy and paste code. Sometimes you just want or need to get it done. Some will not like my use of macros.
New productivity upload: X16 Edit - a text editor
Posted: Sun Oct 03, 2021 6:20 pm
by TomXP411
Yeah, I tend to create a load of macros, as well. By the time I’m done, my programs are more macro than assembly.
Essentially, what this means is I’m making up my own programming language. And I’m fine with that.
New productivity upload: X16 Edit - a text editor
Posted: Mon Oct 04, 2021 3:22 am
by Ed Minchau
I don't use macros at all; my editor doesn't have that feature. I can however copy and paste code.
New productivity upload: X16 Edit - a text editor
Posted: Mon Oct 04, 2021 3:26 am
by Scott Robison
On 10/3/2021 at 9:22 PM, Ed Minchau said:
I don't use macros at all; my editor doesn't have that feature. I can however copy and paste code.
In this case, the macros are part of the assembler. Even notepad can use assembler macros. If the assembler supports, them, that is.