Jump to content
Sign in to follow this  

Adding structured control flow to any assembler

Recommended Posts

If your assembler doesn't already have structured control-flow, e.g. _IF _ELSE _ENDIF _UNTIL _WHILE etc (not to be confused with conditional assembly)

then here's how to add it yourself for free, without needing access to the assembler's source code.




Structured control-flow eliminates the need for most explicit jumps, and the labels they jump to, and thereby makes your code more readable and maintainable.


-- Dave Keenan

Share this post

Link to post
Share on other sites

Ah. Nice. I had run into similar macros a couple of decades ago, and even implemented them for x86 (MASM!), but those implementations were all dependent on being able to implement the "control stack" using fancy symbol/macro definition features that aren't present in many of the current assemblers, and I had never thought of simply using brute force like you do!

Do you happen to have the macros already implemented for the Gnu assembler syntax? That would cover a whole lot of CPUs...

Share this post

Link to post
Share on other sites

Digging up the old code, and looking at your ideas more carefully, I see that they are not so much the same after all. The old code all relied on generating named/numbered symbols that used standard assembler forward referencing, so all it needed was depth counters rather than an actual stack. And it needed the ability to concatenate the counter and the symbol base name within macros. For your amusement, here the meat of the x86 MASM version:

if_ macro condition
begctr= begctr+1
endctr= endctr+1			;; eee
@makjmp BEG,%begctr,JN&condition	;; JNE BEGnnn
savctr= savctr+1
@stnam SAV,%savctr,begctr		;; SAVmmm= nnn
@stnam XXX,%savctr,endctr		;; XXXmmm= eee

else_ macro			;; savctr=mmm, SAVmmm= nnn, begctr=nnn
@setval @val,SAV,%savctr		;;@VAL= SAVmmm(= nnn)
begctr= begctr+1			;;
@makjmp BEG,%begctr,		;; jmp BEGxxx
@stnam BEG,%@val,$			;; BEGnnn= $
@stnam SAV,%savctr,begctr		;; SAVmmm= xxx

endif_ macro			;; savctr=mmm, SAVmmm=xxx, begctr=xxx
@setval @val,SAV,%savctr		;; @VAL= SAVmmm(=xxx)
savctr= savctr-1			;; mmm= yyy
@stnam BEG,%@val,$			;; BEGxxx= $
@setval @val,XXX,%savctr+1		;; @val= XXXmmm
@stnam END,%@val,$			;; ENDeee= $
@stnam SAV,%savctr,begctr		;; SAVyyy= xxx

@setval macro SYM,VAR,IND

@stnam	macro VAR,IND,VAL

Share this post

Link to post
Share on other sites

Thanks for the thanks.


I thank the Forth gurus before me who came up with that elegant minimalist stack-based label-free method for implementing structured control-flow almost on the bare silicon. My only contribution was to realise that since you only need a very shallow stack it could be added to any existing macro assembler, by "brute force" as you say.


Sorry I don't have the macros in Gnu assembler syntax. But for someone familiar with that assembler it should be a piece of cake to translate them. Email me, as per the end of the article, if you need help.


-- Dave Keenan

Share this post

Link to post
Share on other sites

As you mentioned about Forth gurus, Im' the one.

For more than a decade I've been devoting to the design and develop of the assembler for using in microcomputer especially in Forth language. As the time passed by, I found that it also benefits to the assembly language programmer other than forth user.

Here I'll present the assembler that I've developped for various kinds of microcomputer to the world. The assembler in couple with disassembler/decompiler can accomplish the work well. Here you can see some part of the program I've developped for msp430,

hope it'll appeal you to use it in your assembly language coding, you don't need to know Forth to use this software system, but It's good to.


t: '0'~'F'>0~F(16bit) rA+d0 ( 30 ... 0 ) \ assembly; forth

IF'c=1 rA+f6 \ 30~39 => F6~FF

IF'c=0 rA+0a RET \ 0~9

ENDIF' rAn0df \ lower case -> UPPER CASE

rA+f9 \

IF'c=1 rA+fa \ 41~46 => FA~FF

IF'c=0 rAn0f RET \ A~F



ENDIF' rA=0FFFF RET \ error




t: '0'~'F'>0~F(16bit)

( E000 3A 50 D0 00 ) RA+0D0

( E004 11 28 ) E028 c=0?

( E006 3A 50 F6 00 ) RA+0F6

( E00A 03 28 ) E012 c=0?

( E00C 3A 50 0A 00 ) RA+0A

( E010 30 41 ) RET

( E012 3A F0 DF 00 ) RAn0DF

( E016 3A 50 F9 00 ) RA+0F9

( E01A 06 28 ) E028 c=0?

( E01C 3A 50 FA 00 ) RA+0FA

( E020 03 28 ) E028 c=0?

( E022 3A F0 0F 00 ) RAn0F

( E026 30 41 ) RET

( E028 3A 40 FF FF ) RA=FFFF

( E02C 30 41 ) RET

Share this post

Link to post
Share on other sites

Eek! :o I take it all back about it being a piece of cake. Sadly it appears to be impossible to use this method with the gnu assembler.


From the manual:

as is primarily intended to assemble the output of the gnu C compiler gcc for use by the linker ld. Nevertheless, we've tried to make as assemble correctly everything that other assemblers for the same machine would assemble. ...


Unlike older assemblers, as is designed to assemble a source program in one pass of the source file. This has a subtle impact on the .org directive (see .org).


as isn't a real assembler, so don't complain if the .org directive is broken.

Share this post

Link to post
Share on other sites

I finally got around to combining some excellent ideas from @westfwBill Westfield's GNU assembler version with my first version, to describe a method that really will work with ANY assembler that allows macros. Thanks Bill!


And this time I've provided a link to source code (for the IAR/MSP430 version) that should be easily modified for any assembler or target.


Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Create New...