Clean Coding in Xojo: More tips from the community

I hope that doesn’t happen with the various flavours of Exit.

Exit pass by the unwinding (scope clean up) and keep going to the next instruction.
I vagally remember that goto acted differently, just jumped to the outside, probably waiting that the crazy programmer made some weird logic coming back again like:

Var z As Integer = 99
Loop
   z = z - doX()
   Goto wow
  wowback:
   z = z - doY()
Until z = 0
Return

wow:
  z = z - 1
  Goto wowback

A smarter compiler could solve this problem, but Xojo just acted as if it was to return at some point, every time, causing leaks/problems when you just jump out and kept going. :rofl:

Not sure if this is still true, but it used to.

I only used gotos (very much) until 85 due to COBOL and the spaghetti way of thinking from the 70’s still in use. Pascal was going mainstream after 83, structured programming was a thing, and from 86 and up no one wanted to see a Goto in ANY language. :rofl:

This must be it:

https://tracker.xojo.com/xojoinc/xojo/-/issues/24710

Not a lot of detailed investigation except to document that a problem exists - 11 years old and counting.

Informally, I have used Goto in one very specific circumstance - a language parser where performance was greatly improved by Goto-ing my way out of a pile of conditional logic. I’m not aware of any issues it caused, so perhaps my particular usage didn’t trigger any issues; I do remember that it didn’t cross a loop boundary.

Any person that programs Assembly basically writes spaghetti all the time with lots of “gotos”, known as jumps there. And lots of “if last thing ended zero” or “if last thing ended not zero” then jump to “someplace”. That’s how it works, and it won’t change. There, gotos are king. At high level languages they are poison. :smile:

Linux Torvalds uses gotos in C at specific places that he can squeeze few clock pulses to get the kernel faster on purpose.

First program of any size that I wrote, was a histogram package written in assembler for the Sigma-7 - in about 1972 or so. I soon learnt NOT to write spaghetti. It started off like that, but having random jumps just caused me to lose control of what I was doing. So I stopped, thought about a structure, and coded that. That was a lesson I’ve not forgotten; the worst feeling as a programmer is realising that the tiger you are writing has escaped from the cage.

The amusing thing was that the package failed from time to time. I tracked this down to having used 32-bit floating operands, that I’d assumed would be good enough. It wasn’t. But the Sigma-7 had a complete set of 64-bit floating point codes, so I just switched to those and it was fine after that. I’d already learnt in 1966 not to compare floating point numbers for equality.

1 Like

I made a fair bit of money as a Xojo consultant going in and taming those spaghetti code tigers.

1 Like

Show me any usual assembly code you write, and I’ll show a spaghetti. Unless you don’t know what it means…

Assembly does not have If else elseif endif, for-next, etc

Except if you have some precompiler as Structa (presented to me by my old German friend Rainer Gafga (Siemens), probably deceased by now). That allowed us to write structured mixed z80 code as

; if a=0
nop
; else
nop
; endif

but that ended as the usual spaghetti after processed, as

   OR A
   JP NZ, .L38
   nop
   JP .L39
.L38:
   nop
.L39:

So, show me your old code that you said “structured”, and I’ll show you a spaguetti. Because it is the assembly nature, and you did not make magic. It should have lots of “gotos”. Every IF in assembly is also a “GOTO” (jump) causing zigzags (spaghetti) in the code.

1 Like

I make a fair piece of my living writing microcontroller assembly. While “local” jumps are inevitable within subroutines, I always try to structure the higher levels as calls returning values and never jump outside a subroutine.

MAINLOOP:
	clrwdt							; Here and in DELAY are the only places where the wdt is reset.
	rcall	DOTIMERS				; Service timers
	rcall	GETMODE	
	rcall	DOSCAN					; Perform voltage (1.8ms) or continuity (1-6ms) scan
	rcall	CHECKROTATION			; Monitor zero crossings frequently in voltage mode
	rcall	SHOWROTATION			; Update display at rotationupdatetime intervals
	rcall	CHECKBTTRY				; Get and display battery SOC
	rcall	DOAUDIO
	bra		MAINLOOP
1 Like

We, you and I, know that, but now show the CHECKROTATION with a bunch of ifs there and show what I said. :wink:

Of course, like I said, local jumps within subroutines are inevitable. But if you keep the routines reasonably short and minimize exit points, it’s manageable.

CHECKROTATION:
	skpnz	MODE
	return							; Not in voltage mode
	btss	IFS1,#IOCIF				; Any zero crossings?
	return							;	no, MODETIMER will revert to continuity mode if all phases out, and errors will result if one or two phases out
	movw	#modetimeout,MODETIMER	;	yes, restart continuity mode reversion timer

;	Log the last three zero crossings in 2-bit fields of PHASEFLAGS. The concatenated bits are used to determine phase rotation and validity:
;	There are three valid numbers for ABC and three valid numbers for BAC. Using 0-based conductor IDs:

;	L1	L2	L3		00 01 10	0x06
;	L2	L3	L1		01 10 00	0x18
;	L3	L1	L2		10 00 01	0x21
					
;	L3	L2	L1		10 01 00	0x24
;	L2	L1	L3		01 00 10	0x12
;	L1	L3	L2		00 10 01	0x09

	btsc	L1INTF,#L1BITNUM		; Get the conductor ID from the IOC change flags
	mov		#L1,w1
	btsc	L2INTF,#L2BITNUM
	mov		#L2,w1
	btsc	L3INTF,#L3BITNUM
	mov		#L3,w1
GOTID:
	mov		PHASEFLAGS,w0			; Shift 2-bit conductor ID into phase flags' 2 LSBs
	sl		w0,#2,w0
	add		w1,w0,w0
	mov		#0x3F,w1				; Discard oldest (and timeout flag), retain 3 most recent
	and		w0,w1,w0
	mov		w0,PHASEFLAGS

;	We need to filter out spurious results (causes unknown, probably timing isseus) so we require a certain number of repeated results.
;	We can't use the raw PHASEFLAGS codes, however, because they change every pass - we have to convert to ABC/BAC/INV results first.

	.equiv	abc,0
	.equiv	bac,1
	.equiv	invalid,2
	
	switch	PHASEFLAGS
	case	#0x06,GOTABC
	case	#0x18,GOTABC
	case	#0x21,GOTABC

	case	#0x24,GOTBAC
	case	#0x12,GOTBAC
	case	#0x09,GOTBAC
	mov		#invalid,w1			; Invalid sequence
	bra		GOTPHASE
GOTABC:
	mov		#abc,w1
	bra		GOTPHASE
GOTBAC:
	mov		#bac,w1

;	Now we have the decoded phase sequence result, update PHASE only if it stays constant phasecount times
	.equiv	phasecount,16	
	
GOTPHASE:	
	mov		w1,w0
	cp		PREVPHASE		; Change?
	bra		z,SAMEPHASE		;	no, see if stable
	clr		PHASECTR		;	yes, not stable, reset counter
	mov		w0,PREVPHASE	; Update previous to current
	bra		DONESEQ

SAMEPHASE:
	inc		PHASECTR				; Unchanged; stable?
	mov		#phasecount,w0
	cp		PHASECTR
	bra		nz,DONESEQ				;	not yet
	movw	PREVPHASE,PHASE			;	yes, stable, update. PHASE is what SHOWROTATION looks at.
	movw	#phasecount-1,PHASECTR	; Preload counter to fire indefinitely if stays unchanged
	
DONESEQ:
	
;	Clear interrupt flags so we detect next zero crossing
	clr		L1INTF
	clr		L2INTF
	clr		L3INTF
	bclr	IFS1,#IOCIF
	return
1 Like

Anything clever in assembly has zigzags, that’s its nature, and IN ASSEMBLY, it’s simply how things are, and they can be beautiful as they are, even breaking the rules of higher level languages.

image

1 Like

You can write spaghetti in any language. All I meant was that I developed certain personal coding rules to follow when writing assembler.

1 Like

A person can, in most languages, the difference is that if there are ifs in the code, a person can’t avoid a jump/branch/goto in assembly (zigzags/spaghetti).

Q.E.D.

I write a lot of assembly code for embedded controllers, and I know how if/then/else, select/case, for/next, while/wend and other control structures in high level languages look when they’ve been compiled into machine language. In assembly language I write my control structures the same way. Essentially, I can recognize an if/then/else, for/next etc., by how the assembly code is arranged. And any competent assembly language programmer could do the same.

I think it was the first edition of Press & Flannery’s Numerical Recipes where they purposely gave one programming example in an old version of Fortran that had only goto’s for program flow control, because they wanted to demonstrate that you can still write clean understandable code even in such a language. And they did an admirable job of it.

Spaghetti code is when you code poorly regardless of language. When you use best programming practices in a language—even Assembly Language—it’s not spaghetti code.
Mind you, you can invent any definition of ‘spaghetti’ you like for your own use, but don’t expect others to agree with it.

2 Likes

The classical definition of spaghetti is: Not Structured Program. And one basic aspect is “If it uses gotos/branches”

So, if you take that literally, there’s no possibility of assembly code being considered “Structured Programming” due to its nature.

But as the term got a pejorative feeling over the time, people seems attacked when it touches their domains. Don’t feel that way. Hey, I also write low-level code, I’m not offending myself…

Let’s go further. If you take literally of McCabe (Thomas J., Sr.) defined as “subgraphs that breaks the structured programming paradigm” (1976, there are 4 branches that puts any program in the spaghetti domains in his definition), any loop with a premature exit would be, and if elseif. So we need a bit of flexibility here.

BTW, as I did not comment before, Julia’s code is beautifully designed (my opinion). But no one should try to rewrite it as is in a higher-level language. If any higher-level language can be very, very smart, today, it won’t produce a machine code that matches her code.

1 Like

Xojo is not C. Xojo has garbage collection, exception handling, and a shed load of features that take the heavy lifting out of OOP.

And it also has at least one big stinkin’ bug that sidesteps many of those safeguards when Goto is used in certain circumstances (see issue report noted earlier in the thread). That’s what he is referring to.

1 Like

I hope that clears things up.

Just so we’re not spreading misinformation:
Xojo does not have garbage collection.

2 Likes