(*
This is a simple demonstration file for the Zeus assembler.
If you want more sustantial files they're available at www.desdes.com


Macros


From v1.4 onwards Zeus supports parameterless macros.

From v1.7 onwards Zeus provides full macro support including parameters.

From v1.8 onwards Zeus allows string to token conversion so that instructions
can be assembled from expressions and macro parameters.

Macro support took a reasonable amount of effort, despite which I'd advise you all
to avoid using them unless they really help - one of the beauties of assembler
is the way that everything is clearly specified and visible - used carelessly
macros can defeat this. I have seen them add great amounts of confusion to a
source when used badly...

So what sensible uses are there for macros? Well, usually it's adding things
that look like instructions for occasions where the Z80 doesn't actually
have that instruction.

For example, take negate. The Z80 can negate the accumulator, but that's it.

For example, the following is a valid macro definition for a macro called NegHL() :

NegHL		macro()	        ; This doesn't take parameters

		ld a,l		; Code to negate HL
		cpl		;
		ld l,a		;
		ld a,h		;
		cpl		;
		ld h,a		;
		inc hl		;

		mend		; Macro end 

Then Zeus will replace the macro NegHL() with that source, wherever it occurs.

So, if we had the following (rather pointless) code:

		ld hl,$0001	; Make HL 1

		NegHL()		; Plant the code to negate it
		
		NegHL()		; And again...

Zeus would actually generate:

		ld hl,$0001	;

		ld a,l		; Code to negate HL
		cpl		;
		ld l,a		;
		ld a,h		;
		cpl		;
		ld h,a		;
		inc hl		;

		; At this point hl would equal $FFFF

		ld a,l		; Code to negate HL
		cpl		;
		ld l,a		;
		ld a,h		;
		cpl		;
		ld h,a		;
		inc hl		;

		; And hl is back to $0001

And so on.

Note: The macro definition does not have to occur before it is used, though
it will usually save a pass if it does.

Note: The macro definition may include local labels. For example:

PauseA		macro()		;

Lp		dec a		; This "Lp" is local to the macro
		jr nz Lp	; 

		mend		;

In general, all labels defined inside a macro are local to that macro.

Macro definitions cannot be nested, i.e. you cannot define one macro
inside another, but macros can use other macros, and they can recursively
call themselves.

InvL		macro()	        ; No parameters supported (yet)

		ld a,l		; Code to invert L
		cpl		;
		ld l,a		;

		mend		; Macro end 

InvH		macro()	        ; No parameters supported (yet)

		ld a,h		; Code to invert H
		cpl		;
		ld h,a		;

		mend		; Macro end 

; This macro includes other macros

NestedNegHL	macro()	        ; No parameters supported (yet)

		InvL()		; Invert L
		InvH()		; Invert H
		inc hl		; +1

		mend		; Macro end 

NestedNegHL() would produce exactly the same code as NegHL() 


Macros with parameters.


From version 1.7 onwards Zeus supports macro parameters.

This is the source for a macro that takes a single parameter, and which just prints
that parameter.

print	macro(p1)
	  zeusprint p1
          mend  

I hope that is easy to understand - the p1 is just a name used inside the macro to
describe whatever we pass to it. These parameter names are local to the macro.

If that macro exists we can use it as follows:

	print(42)
	print("fred")

Note that the macro can exist anywhere in any of the sources included in the project.
It can be declared before or after use.


But we can pass much more interesting things than just numbers and strings to macros.

Let us also declare a macro that repeats whatever it is passed a number of times

duplicate macro(count,object)	; We're given two parameters

	loop count		; Assume the count is an integer and loop that many times
          object		; Just 'do' whatever we're given each time
          lend			; Loop end

	mend			; Done

Now, look at the 'object' parameter. We don't care what it is, we just give it to
zeus inside the loop and let zeus figure it out each time.

So, this call to the macro, for instance, repeats an "inc a" instruction 8 times

		duplicate(8,inc a)

Do see how it works? When zeus interprets the macro "duplicate" Zeus replaces the word
"count" with the text "8" and the word "object" with the text "inc a" and since that
is inside the loop statement it is used 8 times.

Now, suppose we wanted to use this to repeat the instruction "ld a,b" eight times?
Well, you might think to try duplicate(8,ld a,b) - but that won't work - there's a
pesky comma in the instruction "ld a,b" and zeus will look at that as separating
three different parameters. Since the macro doesn't want three parameters this will
be reported as an error.

Well, perhaps we could try enclosing the ld a,b in quotes? duplicate(8,"ld a,b")
perhaps? Nope. That won't work, but for a different reason - zeus is happy with the
number of parameters, and the macro will repeat "ld a,b" eight times, but the problem
is that "ld a,b" is a string and zeus doesn't automatically look inside strings to
see if they're valid instructions. However, there is a way to group text so that it
can be passed to macros and still considered as instructions; by using the curly braces
like this:

		duplicate(8, { ld a,b } )

Until version 1.7 of zeus curly braces were a variety of comment marker. I think this
is a rather better use for them.

The spaces are optional, I added them to make the text clearer.

Same again, but this time we pass several instructions in the block.
Note the use of ':' here to separate the instructions.

		duplicate(8, { ld a,(hl):ld (de),a:inc hl:inc de } )

But remember that zeus doesn't care much about spaces and line ends, so
why use ":" when we can just have end of lines do the job?

		duplicate(8, {
                          ld a,(hl)
                          ld (de),a
                          inc hl
                          inc de
                          } )

I gave a simple parameterless negate macro above, but a more powerful one might
be written as follows:

First we write a macro to invert any reasonable register. Invert means to change
the state of every bit. This wants to do the following, first if the register is
a it should just use the cpl instruction. If the register is one of the other 8-bit
registers it should load that register into a, cpl it and load it back. If the
register is one of bc,de or hl it should invert both 8-bit registers separately.

This code will do that. It relies on zeus's ability to treat lexical items as strings
when they occur inside expressions (zeus only allows this inside macros, to avoid errors)
which allows you to write simple conditional statements like "if reg=a" to test the reg
parameter...

// Invert a register

Inv	macro(reg)
  	  if reg=a
	    cpl
	  elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l)
	    ld a,reg
	    cpl
	    ld reg,a 
	  elseif reg=bc
	    Inv(c)
	    Inv(b)
	  elseif reg=de
	    Inv(e)
	    Inv(d)
	  elseif reg=hl
	    Inv(l)
	    Inv(h)
          else    
            zeuserror "Inv - this parameter '",reg,"' isn't supported"
	  endif
	mend

Note that Zeus's string comparison is not case-sensitive, so this will work with both
upper and lower case register names.

Note also the "zeuserror" statement. This is used to tell zeus of an error condition.
It takes the same parameters as a zeusprint statement, but it generates an error
message.

Also, notice the recursive calls in there - it calls itself to invert the halves of the
register pairs.

This following Neg macro then uses the Inv macro to handle the inversion part of the
negate process:

// Negate a register

Neg	macro(reg)
	  if reg=a
	    neg
	  elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l)
	    ld a,reg
	    neg
	    ld reg,a
	  elseif (reg=bc) or (reg=de) or (reg=hl)
            Inv(reg)
            inc reg
          else    
            zeuserror "Neg - this parameter '",reg,"' isn't supported"
	  endif	
	mend

Now, this all looks reasonable, but we can improve it slightly. What we would like to
do is take something like, say, hl and extract the h and the l parts for use in other
instructions. This is where the "\" operator comes in - it expects to be followed by a
string and it effectively tells Zeus to look at the text inside the string and use that
as if it was typed into the source. So, for example all the following lines generate
a "ld a,b" instruction (when used inside a macro):

	ld a,b		; Obviously

	ld a,\"b"	; Take the string "b" and use it as a register name.

	ld a,\"bc"[1]	; Take the first character of the string "bc", ie "b"
			; and use it as a register, ie b

	ld a,\b		; Zeus converts register b to a string and then back again

So, we could write a more complete, yet shorter, version of the negate code as follows.

These two macros can both be given either register names or strings as parameters.

// Invert a register. This macro is written so it can handle registers OR strings as parameters

Inv	macro(reg)
  	  if reg=a
	    cpl
	  elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l) or (reg=ixl) or (reg=ixh) or (reg=iyl) or (reg=iyh)
	    ld a,\reg
	    cpl
	    ld \reg,a 
	  elseif (reg=bc) or (reg=de) or (reg=hl)
	    Inv(reg[1])
	    Inv(reg[2])
	  elseif (reg=ix) or (reg=iy)
	    Inv(reg+"l")
	    Inv(reg+"h")
          else    
            zeuserror "Inv - this parameter '",reg,"' isn't supported"
	  endif
	mend

Note the recursive calls to itself to handle the halves of 16-bit register pairs.
These calculate the register names on the fly by chopping up the register names.

// Negate a register. This macro is written so it can handle registers OR strings as parameters

Neg	macro(reg)
	  if reg=a
	    neg
	  elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l) or (reg=ixl) or (reg=ixh) or (reg=iyl) or (reg=iyh)
	    ld a,\reg
	    neg
	    ld \reg,a
	  elseif (reg=bc) or (reg=de) or (reg=hl) or (reg=ix) or (reg=iy)
            Inv(reg)
            inc \reg
          else    
            zeuserror "Neg - this parameter '",reg,"' isn't supported"
	  endif	
	mend

So, to use that, you would put "Neg(a)" or "Neg(ix)" or whatever in your source.




Incidentally, you might be wondering why I haven't spread the long (reg=blah) or (reg=blah)
(etc) expression over two lines? Well, think about it...

No? Look at it like this. Do you think that these two mean the same thing?

	if fred or bert
          halt
	endif
and

	if fred
          or bert
          halt
	endif

Well, what about that "or bert" ? Is it part of the expression, or is it an OR instruction
that is conditional on the previous if statement? It's the second case. It has to be - if zeus
allowed multi-line expressions then any z80 OR, AND and XOR instruction that happened to follow
a line that had an expression would be joined with that expression.

Consider this, for example:

	ld hl,1
	or 2
	
If Zeus let expressions spread across lines then that would be:

	ld hl,1 or 2 ; -> ld hl,3

...which is certainly not what the programmer intended.

Returning to that macro, splitting the long conditional across two lines might give, say:

	  elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l)
              or (reg=ixl) or (reg=ixh) or (reg=iyl) or (reg=iyh)

Which is actually an "elseif (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l)"
followed by an OR instruction "or 0" or "or 1" depending on the parameter.

Take a few moments to grok this because the problem is that it's a silent failure.
Zeus cannot tell you that this is wrong - it isn't. It just doesn't mean what you think
it means (and no, I don't have six fingers... forget it... that isn't important right now)

Now, as a special case, added at the last minute and at great expense, zeus will ignore
line ends in expressions that are contained inside brackets.

So the following source does what you would expect (and works):

	  elseif ( (reg=b) or (reg=c) or (reg=d) or (reg=e) or (reg=h) or (reg=l)
              or (reg=ixl) or (reg=ixh) or (reg=iyl) or (reg=iyh) )

But I'd tend to avoid this form unless you're a crayon-monger and won't forget the brackets
around the expression...


Going back to the Neg macro. There is one other gotcha here that I should mention,
mainly because it got me ;)

What if you try "Neg( (HL) ) ? Would this pass (hl) and try to negate that? Well, no,
not exactly... It'll negate hl. The reason is that as far as Zeus is concerned when it
is processing the text (hl) in the macro that's a hl inside brackets, and zeus not
unreasonably thinks, hmmm, I think the monkey has given me a string expression contained
in brackets - I'll strip out the brackets... Now, it's not going to do that in normal
source, it wouldn't be much use as a z80 assembler if it did, but inside macros, or to
be accurate inside expressions that are inside macros, the distinction between lexical
items and strings is relaxed.

Note also that the '\' string-to-token operator is only allowed inside macros. Basically,
some of the lexical rules inside macros are relaxed; this makes them more powerful at the
risk of encouraging sloppy code.

Also note that zeus dynamically expands macro source. It isn't just a string replacement
carried out before the file is processed - it is carried out while the file is processed.
This means that the parameters can dynamically change between or even during passes.

*)


