Building on those macros, one way to do RANGE_OF is this:
Code:
; RANGE_OF starts a branch
; - replaces top stack item with actual END_CASE label (if first branch)
; writes six lines of code (only five if first branch):
; jmp END_CASE ; previous branch complete - jump to end
; prev_nomatch ; "no match" label from previous branch
; cmp/cpx/cpy ?val_lo ; is this the branch to take ?
; bcc next_nomatch ; no, so try next branch (or done)
; cmp/cpx/cpy (?val_hi)+1
; bcs next_nomatch
RANGE_OF: .macro ?val_lo,?dummy,?val_hi
dyn.poplabel ; pop top stack entry
.if ]DYNLBL$ == "1STCASE" ; first branch?
dyn.makelabel ; make label for END_CASE
.else ; at least one preceeding branch
dyn.put.bra JMP ; write branch to end of CASE structure
.endif
]DYNTMP$ = ]DYNLBL$ ; save END_CASE label locally
dyn.poplabel ; get back previous "no match" label
dyn.put.labelhere ; mark this spot as its destination
dyn.poplabel ; get back compare instruction
dyn.put.cmp ?val_lo ; write compare instruction
dyn.pushlabel ; save compare instruction
dyn.makelabel ; make the next "no match" label
dyn.put.bra BCC ; write a forward branch to it
]DYNTMP2$ = ]DYNLBL$ ; save it locally
dyn.poplabel ; get back compare instruction
dyn.put.cmp (?val_hi)+1 ; write compare instruction
dyn.pushlabel ; save it for future reference
]DYNLBL$ = ]DYNTMP2$ ; get back "no match" label
dyn.put.bra BCS ; write a forward branch to it
dyn.pushlabel ; save it for future reference.
]DYNLBL$ = ]DYNTMP$ ; get back END_CASE label
dyn.pushlabel ; save it for future reference
.endm
Naturally a do-nothing matching "END_RANGE" macro can be defined if desired.
Making the low value, "TO" keyword and high value three separate arguments is the easy way out, of course. A slightly more complicated version can be created that requires a single argument of the form "val_lo TO val_hi", but it requires some modification of the "dyn.put.cmp" macro as well:
Code:
; push back (implied) 6502 compare instruction to input stream
; - "CMP/CPX/CPY #?val" will become the next input line
.macro dyn.put.cmp
.putbacks ]DYNLBL$ ]VAL$
.endm
; RANGE_OF starts a branch
; - replaces top stack item with actual END_CASE label (if first branch)
; writes six lines of code (only five if first branch):
; jmp END_CASE ; previous branch complete - jump to end
; prev_nomatch ; "no match" label from previous branch
; cmp/cpx/cpy ?val_lo ; is this the branch to take ?
; bcc next_nomatch ; no, so try next branch (or done)
; cmp/cpx/cpy (?val_hi)+1
; bcs next_nomatch
RANGE_OF: .macro ?range_expr
.if "?range_expr" !~ /^[0-9]+ +TO +[0-9]+$/i
.error "Range syntax: ?range_expr"
.else
dyn.poplabel ; pop top stack entry
.if ]DYNLBL$ == "1STCASE" ; first branch?
dyn.makelabel ; make label for END_CASE
.else ; at least one preceeding branch
dyn.put.bra JMP ; write branch to end of CASE structure
.endif
]DYNTMP$ = ]DYNLBL$ ; save END_CASE label locally
dyn.poplabel ; get back previous "no match" label
dyn.put.labelhere ; mark this spot as its destination
]VAL$ = match$( "?range_expr", /[0-9]+/ )
dyn.poplabel ; get back compare instruction
dyn.put.cmp ; write compare instruction
dyn.pushlabel ; save compare instruction
dyn.makelabel ; make the next "no match" label
dyn.put.bra BCC ; write a forward branch to it
]DYNTMP2$ = ]DYNLBL$ ; save it locally
]VAL$ = match$( "?range_expr", /[0-9]+$/ ) "+1"
dyn.poplabel ; get back compare instruction
dyn.put.cmp ; write compare instruction
dyn.pushlabel ; save it for future reference
]DYNLBL$ = ]DYNTMP2$ ; get back "no match" label
dyn.put.bra BCS ; write a forward branch to it
dyn.pushlabel ; save it for future reference.
]DYNLBL$ = ]DYNTMP$ ; get back END_CASE label
dyn.pushlabel ; save it for future reference
.endif
.endm
I put that "match$()" function in HXA years ago. I knew I'd have a use for it someday!