;******************************************************************************* ;* Include : BLOCKS.INC ;* Programmer: Tony Papadimitriou <tonyp@acm.org> ;* Purpose : Structured block macros for ASM8 (Win32 & Linux versions, only) ;* Language : Motorola/Freescale/NXP HC08/9S08 Assembly Language (aspisys.com/ASM8) ;* Status : FREEWARE Copyright (c) 2021 by Tony Papadimitriou <tonyp@acm.org> ;* Original : http://www.aspisys.com/code/hc08/blocks.html ;* Note(s) : Use: #Include blocks.inc ;******************************************************************************* ; WORK-IN-PROGRESS: Tested to SOME extent but macros may still contain errors. ; Currently, only the following constructs are implemented: ; [Nested] FOR loop ; [Nested] IF statement ; [Nested] LOOP ... [BREAK] ... ENDLOOP ;******************************************************************************* #Exit _BLOCKS_ _BLOCKS_ #ifmain ;----------------------------------------------------------------------- #ListOff #Uses mcu.inc #ListOn #endif ;------------------------------------------------------------------------ ;******************************************************************************* ; FOR loop (w/ byte control variable) ; The index variable can be accessed using ,SP indexed mode and the symbol name ; created by enclosing the LoopName between underscores, eg. LoopA -> _LoopA_,sp ;******************************************************************************* For.b macro UniqueForLoopName,[#]StartValue,[#]StopValue mset #' ' mreq 1,2,3:UniqueForLoopName,[#]StartValue,[#]StopValue #push #spauto :sp psha ;save user A lda ~2~ ?For~1~.Loop equ *,1 ;byte size cmpa ~3~ !jhi ?For~1~.Exit #pull psha _~1~_ lda 2,asp ;reload user A endm ;******************************************************************************* ; FOR loop (w/ word control variable) ; The index variable can be accessed using ,SP indexed mode and the symbol name ; created by enclosing the LoopName between underscores, eg. LoopA -> _LoopA_,sp ;******************************************************************************* For.w macro UniqueForLoopName,[#]StartValue,[#]StopValue mset #' ' mreq 1,2,3:UniqueForLoopName,[#]StartValue,[#]StopValue #push #spauto :sp pshhx ;save user HX ldhx ~2~ ?For~1~.Loop equ *,2 ;word size cphx ~3~ !jhi ?For~1~.Exit #pull pshhx _~1~_ ldhx 3,asp ;reload user HX endm ;******************************************************************************* ; ENDFOR for either case (byte or word operand) ;******************************************************************************* EndFor macro UniqueForLoopName mreq 1:UniqueForLoopName #if ::?For~1~.Loop = 1 sta 2,asp ;update possibly changed user A pula inca #endif #if ::?For~1~.Loop = 2 sthx 3,asp ;update possibly changed user HX pulhx aix #1 #endif !jmp ?For~1~.Loop ?For~1~.Exit #if ::?For~1~.Loop = 1 pula ;restore updated user A #endif #if ::?For~1~.Loop = 2 pulhx ;restore updated user HX #endif endm ;******************************************************************************* ; IF statement. Usage: @IF Operand1,COMPARATOR,Operand2[,WORD] <code> @ENDIF ; COMPARATOR is one of EQ, NE, GT, GE, LT, LE, HI, HS, LO, LS (as in branches) ; If COMPARATOR is either PL or MI then only Operand1 is used; Operand2 ignored. ; Specify WORD for 4th parm to have a WORD-size comparison. Default is BYTE. ; You can also use the IF.W macro to automatically add the WORD parameter, or ; the IF.B (alias for IF without the WORD parameter) for byte parameters. ;******************************************************************************* IF.W macro Operand1,Comparator,Operand2 mset #' ' mreq 1,2,3:Operand1,Comparator,Operand2 mset 4,WORD @if ~@~ endm ;------------------------------------------------------------------------------- IF.B macro Operand1,Comparator,Operand2 mset #' ' mreq 1,2,3:Operand1,Comparator,Operand2 mset 4 @if ~@~ endm ;------------------------------------------------------------------------------- IF macro Operand1,Comparator,Operand2[,WORD] mset #' ' mreq 1,2,3:Operand1,Comparator,Operand2[,WORD] ?IF_ set :index ?ENDIF_ set ?IF_ #push #spauto :sp #ifparm ~4~ = WORD pshhx ldhx ~1~ #ifnoparm ~2~ = MI #ifnoparm ~2~ = PL #ifparm ~'~,3~'.{:3}~ = x #psp pshhx ldhx 3,asp ;;we must re-load X index ldhx ~3~ pshhx ldhx 3,asp cphx 1,asp ais #:psp #else cphx ~3~ #endif #endif #endif pulhx #else psha lda ~1~ #ifnoparm ~2~ = MI #ifnoparm ~2~ = PL cmpa ~3~ #endif #endif pula #endif #pull mset 1 mset 9,{?IF_} mset 7,eqnegtgeltlehihslolsplmi ;;2-char actions mset 8,neeqleltgegtlslohshimipl ;;their opposites mdo #ifparm ~2~ = ~7.{:mloop-1*2+1}.2~ mset 1,!j~8.{:mloop-1*2+1}.2~ #else mloop :7/2 #endif mreq 1:Unknown comparator \@~2~\@ ~1~ ?IF_~9~ endm ;------------------------------------------------------------------------------- ; ENDIF ENDIF macro #ifndef ?ENDIF_ merror ~0~ must follow an IF #endif mdo #ifz ?ENDIF_ merror Unmatched ~0~ #endif mdef 9,?IF_{?ENDIF_} #ifdef ~9~ ?ENDIF_ set ?ENDIF_-1 mset 9,?IF_{?ENDIF_} mloop #endif ~9~ ?ENDIF_ set ?ENDIF_-1 endm ;******************************************************************************* ; LOOP .. ENDLOOP statement. Usage: @LOOP ... [@BREAK] ... @ENDLOOP ; @BREAK is optional in case we want to exit the loop. ;******************************************************************************* LOOP macro ?LOOP_ set :index ?LOOP_{?LOOP_} endm ;------------------------------------------------------------------------------- ; BREAK BREAK macro mset 0,merror ~0~ must be inside a LOOP ... ENDLOOP #ifndef ?LOOP_{?LOOP_} ~text~ #endif #ifz ?LOOP_ ~text~ #endif !jmp ?ENDLOOP_{?LOOP_} endm ;------------------------------------------------------------------------------- ; ENDLOOP ENDLOOP macro #ifndef ?LOOP_ merror ~0~ must follow a LOOP #endif #ifz ?LOOP_ merror Unmatched ~0~ #endif #ifndef ?LOOP_{?LOOP_} ?LOOP_ set ?LOOP_-1 mtop #endif !jmp ?LOOP_{?LOOP_} #Undef ?LOOP_{?LOOP_} ?ENDLOOP_{?LOOP_} ?LOOP_ set ?LOOP_-1 endm ;******************************************************************************* #Exit ;******************************************************************************* ; Test various macro expansions ;******************************************************************************* CmdBlock macro txa sta ,x ;;just to see it execute aix #1 endm #RAM min rmb 2 max rmb 2 buffer rmb 50 #ROM #spauto Start proc @rsp clra ;(keeps simulator happy) ldhx #buffer @mov.w #1,min @mov.w #5,max ;-------------------------------------- ;FOR loop using variable bounds Test1 @For.w LoopA min max @CmdBlock ;any instructions (not necessarily in a macro) ldhx _LoopA_,sp ;HX = LoopA loop index @EndFor LoopA ;-------------------------------------- ;FOR loop using constant bounds Test2 @For.b LoopB #3 #5 @CmdBlock ;any instructions (not necessarily in a macro) @CmdBlock ;any instructions (not necessarily in a macro) @EndFor LoopB ;-------------------------------------- ;nested FOR loops using both variable and constant bounds Test3 clra ;zero our counter to see how many times inside loops ldhx #12 @For.w Outer min #3 @For.w Inner #4 max @For.b Loop #2 #3 ;as you can see, user registers are not affected by the FOR loop inca ;each time thru the loop, we aix #-1 ;increment A and decrement HX @EndFor Loop @EndFor Inner @EndFor Outer Done cmpa #12 ;expected RegA value jne Error cphx #0 ;expected RegHX value jne Error ;--------------------------------------------------------------------- ; Test the IF/ENDIF macro expansions ;--------------------------------------------------------------------- TestIF @if.w min eq max ;Simple IF (with word comparison) nop @endif ;-------------------------------------- @if #4 ne max ;Nested IF nop @if max gt min ;-+ nop ; | @endif ;-+ nop @endif ;-------------------------------------- @if #1 eq #1 ;Separate nested IFs nop @if min eq #3 ;-+ nop ; | @endif ;-+ nop @if #3 lt #4 ;-+ nop ; | @endif ;-+ nop @endif ;-------------------------------------- ldhx #max ;3rd parm is X indexed @if.w min eq ,x nop @endif ;--------------------------------------------------------------------- ; Test the LOOP/BREAK/ENDLOOP macro expansions ;--------------------------------------------------------------------- lda #3 psha count@@ TestLoop @loop nop @if count@@,sp lt #1 @break @endif dec count@@,sp nop @endloop pula ;-------------------------------------- lda #3 psha OuterCounter TestNestedLoop @loop nop lda #1 psha count@@ @loop @if count@@,sp eq #255 @break @endif dec count@@,sp @endloop pula dec OuterCounter,sp @if OuterCounter,sp eq #0 @break @endif nop @endloop pula ;-------------------------------------- #ifdef @loop @loop @endloop @loop @endloop @endloop #endif ;-------------------------------------- ; @break ;causes error (outside LOOP) ; @endloop ;causes error (unmatched) ;-------------------------------------- bra * Error bra * @vector Vreset,Start