6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Mar 28, 2024 2:07 pm

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: ANN: HXA0.190
PostPosted: Mon Oct 22, 2012 3:54 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
New version posted at http://home.earthlink.net/~hxa/.

New features:

- IFDEF and IFNDEF pseudo ops to check the existence or absence of a global numeric label in the symbol table
- built-in identifying labels __HXA__ (always) and variant identifiers, eg., __HXA_T__
- a LABEL() function to do the same job as IFDEF (just because)

Enhanced features:

- definition of label names changed (again) - fully backward compatible but now it's possible to use variable labels like ']1' and local labels like '@2' (because they look cool)
- anonymous labels can now be colons (:) as well as plus (+) and (-) (because it finally dawned on me that if I treated colons as BOTH forward and backward targets it would be (a) really simple to implement and (b) fully backward compatible and (c) compatible with source from assemblers that only use colons as anonymous labels)

Other less visible changes seem to allow this version to run as fast as the previous few (which was not the case when I first implemented these changes). So I'm reasonably happy with it. And it's only a year plus a few months since Ed asked for some of these! :D


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Oct 22, 2012 8:37 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10760
Location: England
:-) Thanks!


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Oct 22, 2012 10:25 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
I also want to mention that I used these new features to alter a few of the demos. Specifically the demo macro implementations of the 6502, 65C02 and R65C02 instruction sets. They now all have code to detect those built-in labels to determine if the generalized HXA_T or the native HXA65 is being used and adjust a few setups accordingly.

So now the same demo files can be assembled with either version. The most obvious result is that the native version is noticeably faster at the job, maybe five times so (on a slow by today's standards processor, anyway). A more subtle result is that I re-wrote the macro definitions somewhat so now both assemblers produce the same error messages when run against the deliberately error-filled demo files, ie., the error counts and texts are identical. The macro versions are actually a bit talkier because they also produce some warning messages the more no-nonsense native version doesn't.


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sun Dec 23, 2012 5:52 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
New version 0.191 posted at http://home.earthlink.net/~hxa/.

Slight internal changes only. When creating macros for the 65Org16b I noticed it was harder than it needed to be tracking down errors related to unclosed macro definitions, especially when there were lots and lots of macros being defined. This was because even when the definition was incomplete the macro name itself was still recognized as being a macro name, and so using it elsewhere didn't cause an error. I had a reason for that, but I can't remember what it was, and whatever it was it can't outweigh the nuisance factor it created. So now only completely defined macros are recognized as macros.

But that was just annoying. I also fixed an actual bug, that macro names declared in the label field could not end in colons. The documentation says they can, but apparently I never tested that ( :oops: ).

I discovered this while playing around with converting Garth Wilson's structured macros to HXA's capabilities. I fixed the bug, and I think the conversion went rather well. I've attached some results; there's another form of them now included as a processor demo in the download. I see Garth is about to define some new macros, but they're not included because I don't know what they are!


Attachments:
strucmac.zip [11.73 KiB]
Downloaded 528 times
Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sun Dec 23, 2012 8:03 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
Quote:
I see Garth is about to define some new macros, but they're not included because I don't know what they are!

I've been working on a project with a PIC16F72 where I'm trying to kill as many birds with one stone as possible, one being to get more structure macro experience in assembly and see what other structures I would want and add them and make sure they work right.  One I've done there is FOR...NEXT for single-byte counters.  I have one for two-byte variables in the structure macros 6502 page, but I need to add ones for single bytes and especially X and Y that will work similarly to the PIC ones below, able to count up and down, and be nestable with other identical ones.  The PIC doesn't really have index registers so the loop counter pretty much has to be in a RAM variable; but the 6502 frequently uses X or Y as a loop counter.

My PIC ones so far (with syntax examples buried in the code) (Darn!  I thought the code portions were not supposed to wrap lines!):
Code:

 ;                       +--------------------+
 ;                       |     FOR...NEXT     |
 ;                       +--------------------+


 ; FOR...NEXT is nestable.  Its limitations are:
 ;    1. The counter has to be a file register.
 ;    2. The index must start as a constant, or it can start as W.  The macro cannot fetch it from a variable.
 ;    3. The limit must start as a constant, ie, not a value computed in the program or fetched from a variable.
 ;    4. It is limited to one-byte counters.
 ;    5. W gets altered in setting up the loop unless the initial index value comes in on W.
 ;    6. W gets altered by NEXT if the limit is non-0.
 ;    7. Since the INC or DEC in NEXT does the comparing and drops through if there's a match, the loop will not be run with
 ;        the final "to" value.  IOW,  "FOR  COUNT, 8, DOWN_TO, 0" will run 8 times, not 9:  8, 7, 6, 5, 4, 3, 2, and 1, not 0.

 ; LEAVE_LOOP could be implemented, but since it would always be used with a condition, maybe it would be best to just use an
 ; IF_<condx>...END_IF  to complete the loop.  Otherwise, what you could do is use a fourth stack level, and have FOR init it
 ; as 0.  Then if there's a LEAVE_LOOP, it would store the address of its GOTO instruction in that stack cell, and NEXT would
 ; test it to see if that cell is non-0 and fill it in with a GOTO to the end if so.  You would have to be careful not to put
 ; the LEAVE_LOOP inside another structure that might be using the macro structure stack.  And as always, "compiler" security
 ; is up the to programmer.

 ; The number of instruction cycles taken for a loop loading its own index (call it "N") into W and decrementing it to 0 is:
 ;
 ;    2                         for loading W and storing it in the variable
 ;  + N * loop_contents         ie, your code in the loop, which might be 0 if it's a do-nothing loop for delay only
 ;  + (N-1) * 3                 for DECFSZ and GOTO top_of_loop
 ;  + 2                         for final DECFSZ skipping over the GOTO
 ;
 ; So for:
 ;     FOR  LOOP_COUNT, 8, DOWN_TO, 0
 ;     NEXT LOOP_COUNT
 ; you have 2 + 0 + 21 + 2 = 25 instruction cycles, or 100 clocks for this empty loop.



DOWN_TO:        EQU   0         ; These two are used in the FOR-NEXT structure below which uses DECFSZ
UP_TO:          EQU   1         ; and INCFSZ.  It needs to know which one to use.  This and the limit and
                                ; the file register addr get put on the stack since FOR-NEXT is nestable.


FOR:            MACRO var_name, index, action, limit   ; Syntax example:   FOR  LOOP_COUNT, 8, DOWN_TO, 0   (1-byte counter only.)
                IF  index != W          ; The .INC file assigns a value of 0 to W, and it's better to allow the index to
                    MOVLW index         ; start with the value of W than to allow counting from 0 to 0.
                ENDIF
                MOVWF var_name          ; The variable name doesn't need to get stored since we will require NEXT to include
                                        ; the name, and an "I", "J", etc. reference can just use the variable name also.
                STACK_PUSH
                STACK_PUSH              ; Add three cells to the stack.
                STACK_PUSH
STK_LVL_1:      SET   $                 ; This is the addr of the top of the loop for NEXT to branch up to.
STK_LVL_2:      SET   action            ; Record whether the NEXT should INC or DEC.
STK_LVL_3:      SET   limit             ; Record what ending number NEXT should be watching for.
                ENDM
 ;--------------------------

NEXT:           MACRO var_name                                          ; Syntax example:   NEXT  LOOP_COUNT

                IF  (STK_LVL_2 == DOWN_TO) && (STK_LVL_3 == 0)          ; MPASM does not allow nested IF...ENDIF
                    DECFSZ  var_name,F                                  ; conditional assembly, so do it this way.
                ENDIF

                IF  (STK_LVL_2 == UP_TO) && (STK_LVL_3 == 0)
                    INCFSZ  var_name,F
                ENDIF

                IF  (STK_LVL_2 == DOWN_TO) && (STK_LVL_3 != 0)
                    DECF  var_name,F
                    MOVF  var_name,W
                    SUBLW STK_LVL_3
                    BTFSS STATUS,Z
                ENDIF

                IF  (STK_LVL_2 == UP_TO) && (STK_LVL_3 != 0)
                    INCF  var_name,F
                    MOVF  var_name,W
                    SUBLW STK_LVL_3
                    BTFSS STATUS,Z
                ENDIF
                                                ; Unless the limit has been reached and the above instruction
                GOTO STK_LVL_1                  ; branches around this, branch back up to the top of the loop.
                STACK_POP2
                STACK_POP
                ENDM
 ;--------------------------

Another set will be for testing individual bits of absolute addresses, similar to BBR and BBS in ZP.  Again my PIC version which I will later carry out in 6502 also is:
Code:
IF_BIT:         MACRO  FL_ADR, BIT_NR, CONDX    ; (tests a bit in a file register)       Syn:  IF_BIT  INTCON, T0IF, IS_SET
                IF CONDX == IS_SET
                   BTFSS  FL_ADR,BIT_NR
                ELSE
                   BTFSC  FL_ADR,BIT_NR
                ENDIF
                STACK_PUSH              ; Add a cell to the stack, and
STK_LVL_1:      SET  $                  ; put the current address in that cell.  A GOTO instruction will go at this address.
                RES  1                  ; REServe 1 program word space, like ORG $+1, for a GOTO to be added here later, either
                ENDM                    ;                                       by the ELSE_ (if there is one), or the END_IF.
 ;--------------------------

and an example of the usage is (and as you can see, it's in part of a CASE statement):
Code:
           CASE_OF  4                           ; We were waiting for the low address byte to be shifted out.
               RAM_BANK  1                      ; Is that finished yet?  (Check buffer-full bit in SSP status register.)
               IF_BIT  SSPSTAT, BF, IS_SET      ; If so,
                   RAM_BANK  0
                   CLRF   SSPBUF                ; send out a dummy byte in order to get the first data byte to read,
                   INCF   FLASH_RD_STATE,F      ; then increment the state.
               END_IF
           END_OF

In this case, the serial-port shifting for the given hardware takes time, and there's too much to do to twiddle our thumbs, so I have kind of a multitasking system where every time a task is called up, it looks to see if there's anything it can or should do at the moment, and if not, lets the computer move on to do other necessary things while it's waiting.  [Edit, 5/15/14: I posted an article on simple methods of doing multitasking without a multitasking OS, at http://wilsonminesco.com/multitask/index.html.]

Curiously, the first time I used a CASE statement, I had 10 different cases to watch for, and 10 is how many I made the macros to be able to do; so I guess I will be increasing the maximum to 16.  (If the cases are sequential numbers, the 6502 does well with a jump table; but the PIC's jump-table ability is more limited.)  This portion of the code goes two pages without needing a single label, because of the program structures.

There will be others.  There are so many things I have on the burners, with one of the goals being to eventually be able to offer a complete handheld 6502/816 workbench computer with its own keyboard, display, flash file storage, and a reasonably tough case, a computer which the user can, if necessary, develop software on without needing a host computer like a PC (although that will still be possible).

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Fri Dec 28, 2012 3:05 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Garth,

The #v(x) feature of MPASM is very useful for building structured program macros for PICs. This is how I did mine.
Code:
;===============================================================================
; IF/ELSE/ENDIF
;-------------------------------------------------------------------------------

_IFZ            macro
                nolist
                __IF    btfss,Z
                endm

_IFNZ           macro
                nolist
                __IF    btfsc,Z
                endm

_IFC            macro
                nolist
                __IF    btfss,C
                endm

_IFNC           macro
                nolist
                __IF    btfsc,C
                endm

_IFDC           macro
                nolist
                __IF    btfss,DC
                endm

_IFNDC          macro
                nolist
                __IF    btfsc,DC
                endm

__IF            macro   _OPCODE,_FLAG
                local   __JUMP
__JUMP          =       __NEXT
__NEXT          +=      .1
__ISTK          +=      .1
__IS#v(__ISTK)  =       __JUMP

                list
                _OPCODE STATUS,_FLAG
                goto    __L#v(__JUMP)
                endm

;-------------------------------------------------------------------------------

_ELSE           macro
                nolist

                if      __ISTK == .0
                error   "ELSE cannot be used when no IF is active"
                endif

                local   __JUMP
                local   __ELSE
__ELSE          =       __IS#v(__ISTK)
__JUMP          =       __NEXT
__NEXT          +=      .1
__IS#v(__ISTK)  =       __JUMP

                list
                goto    __L#v(__JUMP)
__L#v(__ELSE):
                endm

;-------------------------------------------------------------------------------

_ENDIF          macro
                nolist

                if      __ISTK == .0
                error   "ENDIF cannot be used when no IF is active"
                endif

                local   __JUMP
__JUMP          =       __IS#v(__ISTK)
__ISTK          -=      .1

                list
__L#v(__JUMP):
                endm

;===============================================================================
; REPEAT .. UNTIL/FOREVER
;-------------------------------------------------------------------------------

_REPEAT         macro
                nolist

                local   __JUMP
__JUMP          =       __NEXT
__NEXT          +=      .2
__LSTK          +=      .1
__LS#v(__LSTK)  =       __JUMP

                list
__L#v(__JUMP):
                endm

;-------------------------------------------------------------------------------

_UNTILZ         macro
                nolist
                __UNTIL btfss,Z
                endm

_UNTILNZ        macro
                nolist
                __UNTIL btfsc,Z
                endm

_UNTILC         macro
                nolist
                __UNTIL btfss,C
                endm

_UNTILNC        macro
                nolist
                __UNTIL btfsc,C
                endm

_UNTILDC        macro
                nolist
                __UNTIL btfss,DC
                endm

_UNTILNDC       macro
                nolist
                __UNTIL btfsc,DC
                endm

                if      __ARCH16

_UNTILOV        macro
                nolist
                __UNTIL btfss,OV
                endm

_UNTILNOV       macro
                nolist
                __UNTIL btfsc,NOV
                endm

                endif

__UNTIL          macro   _COND
                if      __LSTK == .0
                error   "UNTIL cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)
__LSTK          -=      .1

                list
                _OPCODE STATUS,_FLAG
                goto    __L#v(__JUMP)
__L#v(__JUMP+.1):
                endm

;-------------------------------------------------------------------------------

_FOREVER        macro
                nolist

                if      __LSTK == 0
                error   "FOREVER cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)
__LSTK          -=      .1

                list
                goto    __L#v(__JUMP)
__L#v(__JUMP+.1):
                endm

;===============================================================================
; WHILE .. WEND
;-------------------------------------------------------------------------------

_WHILEZ         macro
                nolist
                __WHILE btfss,Z
                endm

_WHILENZ        macro
                nolist
                __WHILE btfsc,Z
                endm

_WHILEC         macro
                nolist
                __WHILE btfss,C
                endm

_WHILENC        macro
                nolist
                __WHILE btfsc,C
                endm

_WHILEDC        macro
                nolist
                __WHILE btfss,DC
                endm

_WHILENDC        macro
                nolist
                __WHILE btfsc,DC
                endm

__WHILE         macro   _OPCODE,_FLAG
                local   __JUMP
__JUMP          =       __NEXT
__NEXT          +=      .2
__LSTK          +=      .1
__LS#v(__LSTK)  =       __JUMP

                list
__L#v(__JUMP):
                _OPCODE STATUS,_FLAG
                goto    __L#v(__JUMP+.1)
                endm

;--------------------------------------------------------------------------------

_WEND           macro
                nolist

                if      __LSTK == 0
                error   "WEND cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)
__LSTK          -=      .1

                list
                goto    __L#v(__JUMP)
__L#v(__JUMP+.1):
                endm

;===============================================================================
; BREAK/CONTINUE
;-------------------------------------------------------------------------------

_CONTINUE       macro
                nolist

                if      __LSTK == 0
                error   "CONTINUE cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)

                list
                goto    __L#v(__JUMP)
                endm

;-------------------------------------------------------------------------------

_BREAK          macro
                nolist

                if      __LSTK == 0
                error   "BREAK cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)

                list
                goto    __L#v(__JUMP+.1)
                endm

_BREAKZ         macro
                nolist
                __BREAK btfsc,Z
                endm

_BREAKNZ        macro
                nolist
                __BREAK btfss,Z
                endm

_BREAKC         macro
                nolist
                __BREAK btfsc,Z
                endm

_BREAKNC        macro
                nolist
                __BREAK btfss,Z
                endm

_BREAKDC         macro
                nolist
                __BREAK btfsc,Z
                endm

_BREAKNDC       macro
                nolist
                __BREAK btfss,Z
                endm

__BREAK         macro   _OPCODE,_FLAG

                if      __LSTK == 0
                error   "BREAK cannot be used when no loops are active"
                endif

                local   __JUMP
__JUMP          =       __LS#v(__LSTK)

                list
                _OPCODE STATUS,_FLAG
                goto    __L#v(__JUMP+.1)
                endm

;===============================================================================
; SWITCH .. CASE .. ENDCASE CASE .. ENDCASE .. DEFAULT .. ENDSWITCH
;-------------------------------------------------------------------------------

_SWITCH         macro
                nolist

                local   __JUMP
__JUMP          =       __NEXT
__NEXT          +=      .1
__SSTK          +=      .1
__SS#v(__SSTK)  =       __JUMP
__VS#v(__SSTK)  =       .0

                list
                endm

;-------------------------------------------------------------------------------

_CASE           macro   _VALUE
                nolist

                local   __JUMP
__JUMP          =       __NEXT
__NEXT          +=      .1
__CS#v(__SSTK)  =       __JUMP
__VS#v(__SSTK)  ^=      _VALUE

                list
                xorlw   .#v(__VS#v(__SSTK))
                btfss   STATUS,Z
                goto    __L#v(__JUMP)
                endm

;-------------------------------------------------------------------------------

_ENDCASE        macro
                nolist

                local   __CASE
                local   __JUMP
__JUMP          =       __SS#v(__SSTK)
__CASE          =       __CS#v(__SSTK)

                list
                goto    __L#v(__JUMP)
__L#v(__CASE):
                endm

;-------------------------------------------------------------------------------

_DEFAULT        macro
                endm

;-------------------------------------------------------------------------------

_ENDSWITCH      macro
                nolist

                local   __JUMP
__JUMP          =       __SS#v(__SSTK)
__SSTK          -=      .1

                list
__L#v(__JUMP):
                endm

; Initiase the control structure stack pointers and counters

__NEXT          set     .0              ; Counter for label creation
__ISTK          set     .0              ; Stack pointer for IF/ELSE/ENDIF
__LSTK          set     .0              ; Stack pointer for REPEAT & WHILE
__SSTK          set     .0              ; Stack pointer for SWITCH

                list

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Fri Dec 28, 2012 9:05 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
BitWise, thanks for the encouragement to look into the #v(x) feature further.  It looks like you're using two or three different stacks.  Is that right?  And what is the .1?  I haven't seen anything about decimal places in the manual.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sat Dec 29, 2012 12:16 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
My thread has been hijacked! Well, okay....

Code:
;-------------------------------------------------------------------------------

_DEFAULT        macro
                endm

;-------------------------------------------------------------------------------



I see you noticed that, too.

Garth, I did not see any mention on your website, but have you considered that because your "CASE_OF" macro transfers control to "END_OF" when a comparison fails, any code placed between the last "END_OF" and "END_CASE" amounts to default processing? The above macro is essentially syntactic sugar to point out that's what's happening. I did the same thing in my version of your macros, except I called it "CASE_ELSE".


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sat Dec 29, 2012 12:24 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
Yes, that is the intention.  In my web page on it, the example I gave was for outputting characters to a display, where in the case of $0A (LF), you do one thing, in the case of $0D (CR), you do another, and in the case of 08 (BkSpc) you do another, and if it's anything else, you do the default.  If you wish to clarify (although knowledge of the structure doesn't require it), you could have a do-nothing macro called CASE_ELSE or ELSE_OF or something like that.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sat Dec 29, 2012 12:24 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
GARTHWILSON wrote:
BitWise, thanks for the encouragement to look into the #v(x) feature further. It looks like you're using two or three different stacks. Is that right? And what is the .1? I haven't seen anything about decimal places in the manual.

Yes, each type of control structure has its own stack.

The . forces decimal in MPASM regardless of the current radix (which defaults to hex)

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Wed Jan 02, 2013 12:03 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
Aw, no one's even looked at my lovely macros! Very well, I'll take my cue from these other posts and inflict them anyway :D

Code:
; Garth Wilson's 6502 Structure Macros
; - reference: http://wilsonminesco.com/StructureMacros/index.html

; by Anton Treuenfels

; first created: 12/03/12
; last revised:  12/28/12

; - externally these versions behave the same way as GWilson's
; - internally these versions are modified for use with HXA65x assemblers
; - additional modifications:
; - changed BRA (65C02) to JMP (6502) where BRA used
; - changed REPEAT to REPEAT_ to avoid built-in pseudo op name conflict
; - added do-nothing CASE_ELSE macro (see CASE structure notes)

; INCLUDE this file to use these macros

; -------------------------------

        .if ver() < $0191
        .fatal  "HXA version 0.191 or higher required"
        .endif

   .readonce

; -------------------------------

; 51 macros:
;     IF_EQ
;     IF_ZERO
;     IF_NEQ
;     IF_NOT_ZERO
;     IF_PLUS
;     IF_MINUS
;     IF_NEG
;     IF_C_SET
;     IF_C_CLR
;     IF_V_SET
;     IF_V_CLR
;     IF_GE     ; These two should refer
;     IF_LT     ; to the C flag.
;     ELSE_
;     END_IF
;
;     BEGIN
;     WHILE_EQ
;     WHILE_ZERO
;     WHILE_NOT_ZERO
;     WHILE_NEQ
;     WHILE_PLUS
;     WHILE_MINUS
;     WHILE_NEG
;     WHILE_C_CLR
;     WHILE_LT
;     WHILE_C_SET
;     WHILE_GE
;     WHILE_V_CLR
;     WHILE_V_SET
;     REPEAT
;     AGAIN
;     UNTIL_EQ
;     UNTIL_ZERO
;     UNTIL_NEQ
;     UNTIL_NOT_ZERO
;     UNTIL_PLUS
;     UNTIL_MINUS
;     UNTIL_NEG
;     UNTIL_C_CLR
;     UNTIL_LT
;     UNTIL_C_SET
;     UNTIL_GE
;     UNTIL_V_CLR
;     UNTIL_V_SET
;
;     CASE
;     CASE_OF
;     END_OF
;     CASE_ELSE
;     END_CASE
;
;     FOR
;     NEXT
; -------------------------------

; - GWilson's macros dynamically manipulate the program counter
; in order to make sure control transfers happen properly
; - these versions instead use dynamically generated labels
; to let HXA handle control transfers properly at assembly time
; - GWilson's macros "fake" a stack (used to make sure nested
; control structures unwind properly) by re-defining the
; contents of certain numeric labels in a particular order
; - these versions instead "fake" a stack using a single string,
; adding and taking away labels from one end

; the basic approach here (aka the "trick") makes use of some of HXA's features:

; - first, because all labels are sequences of characters,
; they themselves are strings, and can be manipulated as strings:
; - 1) we can create strings on the fly and,
; - 2) we can create strings which have the form of labels, and
; - 3) we can store and recall them to and from variable string labels, and
; - 4) a variable string label used in a string expression evaluates to its contents

; - second, HXA's ".putbacks" pseudo op evaluates its argument as a string expression
; and then "puts back" the result onto the input stream
; - the evaluated string expression thus becomes the next input line

; - so: we can create, store, recall, manipulate and use labels at will


; -------------------------------
; "housekeeping" macros
; -------------------------------

; dynamic labels have the form "__xxxxx", where "xxxxx"
; is an ever-increasing number starting at ten thousand (10000)
; - the form is a "global numeric" label having global scope
; (variable, local and anonymous won't do for this purpose)
; - HXA will assign the current value of the program counter
; to them when they appear on an input line by themselves

; internal tracking variables (don't access directly!)
; - labels beginning with "]" are "variable"
; and can be assigned new values at any time
; - labels ending in "$" are string values,
; otherwise they are numeric (integer, actually)

]DYNCNT     = 9999   ; incremented for each new label
DYNLEN       = 7      ; the length of a dynamic label
]DYNSTK$    = ""   ; the label "stack"
]DYNLBL$    = ""   ; the current dynamic label name
]DYNTMP$    = ""   ; temporary storage for label name

        ; create the next dynamic label
   ; - increment count, then convert to string and add underscores

        .macro dyn.makelabel
]DYNCNT     = ]DYNCNT+1
]DYNLBL$    = "__" str$(]DYNCNT)
        .endm

        ; stack the current dynamic label
   ; - "pushes" by concatenation
   ; - max depth is at least int(2000/DYNLEN) (MS-DOS version)

        .macro  dyn.pushlabel
]DYNSTK$    = ]DYNLBL$ ]DYNSTK$
        .endm

        ; unstack the top dynamic label to a named label
   ; - up to 90000 dynamic labels can be generated before
   ; their length changes and this code won't work

        .macro  dyn.poplabel
        .if !]DYNSTK$            ; stack empty?
        .fatal "Label stack empty"
        .else
]DYNLBL$    = mid$( ]DYNSTK$, 1, DYNLEN )   ; copy top label to current label
]DYNSTK$    = mid$( ]DYNSTK$, DYNLEN+1 )    ; "pop" it from "stack"
        .endif
        .endm

; -------------------------------

   ; push back 6502 branch instruction with (implied) label argument to input stream
   ; - "?mnemonic __xxxxx" will become next input line

   .macro dyn.ins.bra, ?mnemonic
   .putbacks "?mnemonic " ]DYNLBL$
   .endm

   ; push back (implied) label argument to input stream
   ; - "__xxxxx" will become next input line

   .macro dyn.ins.labelhere
   .putbacks ]DYNLBL$
   .endm
   
; -------------------------------

;                         +--------------------------+
;                         |  IF_xx...ELSE_...END_IF  |
;                         +--------------------------+

IF_EQ:      MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BNE   ; write a forward branch to it
       dyn.pushlabel   ; save it for future reference
            ENDM
 ;----------------

ELSE_:      MACRO               ; Don't forget the "_" in "ELSE_" ! ! !  (to differentiate from the assembler's conditional)
       dyn.makelabel   ; make a new label for end of FALSE branch
       dyn.ins.bra JMP   ; write a forward branch to it
]DYNTMP$   = ]DYNLBL$      ; save new label for a moment
       dyn.poplabel   ; get label for end of TRUE branch back
       dyn.ins.labelhere   ; mark this spot
]DYNLBL$    = ]DYNTMP$      ; restore new label
       dyn.pushlabel   ; save it for future reference
            ENDM
 ;----------------

END_IF:     MACRO               ; The similar directive in the HXA assembler is ENDIF, so there's no mix-up here.
       dyn.poplabel   ; get label for end of "IF__" block back (TRUE or FALSE branch; doesn't matter)
       dyn.ins.labelhere   ; mark this spot (no code generated)
            ENDM
 ;----------------

IF_ZERO:    MACRO      ; same as IF_EQ
       IF_EQ
            ENDM
 ;----------------

IF_NEQ:     MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BEQ   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_NOT_ZERO: MACRO      ; same as IF_NEQ
        IF_NEQ
        ENDM
 ;----------------

IF_PLUS     MACRO      ; uses N flag
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BMI   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_MINUS:   MACRO      ; uses N flag
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BPL   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_NEG:     MACRO      ; same as IF_MINUS
       IF_MINUS
       ENDM
 ;----------------

IF_C_SET:   MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BCC   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_C_CLR:   MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BCS   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_GE:      MACRO      ; same as IF_C_SET
       IF_C_SET
       ENDM
 ;----------------

IF_LT:      MACRO      ; same as IF_C_CLEAR
       IF_C_CLR
            ENDM
 ;----------------

IF_V_SET:   MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BVC   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM
 ;----------------

IF_V_CLR:   MACRO
       dyn.makelabel   ; make a new label for end of TRUE branch
       dyn.ins.bra BVS   ; write a forward branch to it
       dyn.pushlabel   ; stack it
            ENDM

; -------------------------------

;                        +--------------------------+
;                        |      BEGIN...AGAIN       |
;                        |      BEGIN...UNTIL       |
;                        |  BEGIN...WHILE...REPEAT  |
;                        +--------------------------+

BEGIN:      MACRO                   ; For use with AGAIN, REPEAT, and any of the UNTIL_xx and WHILE_xx macros.
       dyn.makelabel           ; make a new label
       dyn.ins.labelhere       ; use it to mark this spot
       dyn.pushlabel       ; save it for future reference
            ENDM   
 ;----------------

UNTIL_EQ:   MACRO                   ; Continue loop until Z flag is set.
       dyn.poplabel       ; get BEGIN label back
       dyn.ins.bra BNE       ; write a backward branch to it
            ENDM
 ;----------------

AGAIN:      MACRO                   ; Continue loop unconditionally.  Similar to REPEAT, but not for use with WHILE_xx.
       dyn.poplabel            ; get BEGIN label back
       dyn.ins.bra JMP         ; write a backward branch to it
            ENDM
 ;----------------

WHILE_NEQ:  MACRO                   ; Continue while Z flag is clear.  For use with BEGIN and REPEAT.
       dyn.makelabel       ; make a new label for end of REPEAT
       dyn.ins.bra BEQ       ; write a forward branch to it
       dyn.pushlabel           ; save it for future reference
       ENDM
 ;----------------

REPEAT_:    MACRO                   ; Similar to AGAIN, but for use with WHILE_xx.
            dyn.poplabel       ; get back forward WHILE label
]DYNTMP$    = ]DYNLBL$          ; save it for a moment
       dyn.poplabel       ; get back backward BEGIN label
       dyn.ins.bra JMP       ; write a backward branch to it
]DYNLBL$    = ]DYNTMP$          ; restore WHILE label
       dyn.ins.labelhere       ; mark this spot
            ENDM
 ;----------------

WHILE_EQ:   MACRO                   ; Continue while Z flag is set.  For use with BEGIN and REPEAT.
       dyn.makelabel       ; make a new label for end of REPEAT
       dyn.ins.bra BNE       ; write a forward branch to it
       dyn.pushlabel           ; save it for future reference
       ENDM
 ;----------------

WHILE_ZERO: MACRO                   ; same as WHILE_EQ
       WHILE_EQ
       ENDM
 ;----------------

WHILE_NOT_ZERO: MACRO               ; same as WHILE_NEQ
           WHILE_NEQ
           ENDM
 ;----------------

WHILE_MINUS: MACRO                  ; Continue while N flag is set.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BPL       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

WHILE_NEG:  MACRO                   ; same as WHILE_MINUS
       WHILE_MINUS
       ENDM
 ;----------------

WHILE_PLUS:  MACRO                  ; Continue while N flag is clear.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BMI       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

WHILE_C_SET: MACRO                  ; Continue while C flag is set.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BCC       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

WHILE_GE:   MACRO                   ; same as WHILE_C_SET
       WHILE_C_SET
       ENDM
 ;----------------

WHILE_C_CLR: MACRO                  ; Continue while C flag is clear.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BCS       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

WHILE_LT:   MACRO                   ; same as WHILE_C_CLR
       WHILE_C_CLR
       ENDM
 ;----------------

WHILE_V_SET: MACRO                  ; Continue while V flag is set.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BVC       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

WHILE_V_CLR: MACRO                  ; Continue while V flag is clear.  For use with BEGIN and REPEAT.
        dyn.makelabel       ; make a new label for end of REPEAT
        dyn.ins.bra BVS       ; write a forward branch to it
        dyn.pushlabel          ; save it for future reference
        ENDM
 ;----------------

UNTIL_ZERO: MACRO          ; same as UNTIL_EQ
       UNTIL_EQ
       ENDM
 ;----------------

UNTIL_MINUS:  MACRO                 ; Continue loop until N flag is set
         dyn.poplabel       ; get BEGIN label back
         dyn.ins.bra BPL       ; write a backward branch to it
              ENDM
 ;----------------

UNTIL_NEG:  MACRO                ; same as UNTIL_MINUS
       UNTIL_MINUS
       ENDM
;----------------

UNTIL_PLUS: MACRO                   ; Continue loop until N flag is clear
       dyn.poplabel       ; get BEGIN label back
       dyn.ins.bra BPL       ; write a backward branch to it
            ENDM
 ;----------------

UNTIL_C_SET:  MACRO                 ; Continue loop until C flag is set
         dyn.poplabel       ; get BEGIN label back
         dyn.ins.bra BCC       ; write a backward branch to it
              ENDM
 ;----------------

UNTIL_GE:   MACRO                ; same as UNTIL_C_SET
       UNTIL_C_SET
       ENDM
 ;----------------

UNTIL_C_CLR:  MACRO                 ; Continue loop until C flag is clear
         dyn.poplabel       ; get BEGIN label back
         dyn.ins.bra BCC       ; write a backward branch to it
              ENDM
 ;----------------

UNTIL_LT:   MACRO                ; same as UNTIL_C_CLR
       UNTIL_C_CLR
       ENDM

 ;----------------

UNTIL_V_SET:  MACRO                 ; Continue loop until V flag is set
         dyn.poplabel       ; get BEGIN label back
         dyn.ins.bra BVC       ; write a backward branch to it
              ENDM
 ;----------------

UNTIL_V_CLR:  MACRO                 ; Continue loop until V flag is clear
         dyn.poplabel       ; get BEGIN label back
         dyn.ins.bra BVS       ; write a backward branch to it
              ENDM

; -------------------------------

;                        +--------------------+
;                        |   CASE...END_CASE  |
;                        +--------------------+

 ; For this version using JMPs at the END_OFs, the only places that branch distances might be excessive is the BNEs in CASE_OF,
 ; and it's pretty unlikey that those would be a problem.
 ; Nested CASE statements are not permitted; ie, between CASE and END_CASE, you can't have another CASE...
 ; You can however nest a CASE structure with other types of structures.

]CASINS$   = ""            ; CMP, CPX or CPY
]CASEND$   = ""            ; label for the end of case structure (END_CASE macro)

; this version behaves much as GWilson's original:
; - the CASE_OF "no match" branch leads to the next END_OF (to just after the "JMP" instruction it writes)
; - if a CASE_OF immediately follows an END_OF all will be well
; - however if code follows an END_OF it will be executed:
; - 1) if not the last END_OF, care must be taken to preserve the register value being compared so the next CASE_OF works
; - 2) if it is the last END_OF, the code amounts to default processing which happens if no CASE_OF is matched
; - unless code follows the last END_OF, the "JMP" instruction it writes is spurious, since it only goes to END_CASE
; - a difference: CASE_ELSE is added, which does nothing but mark that spot in the source code (no object code produced)
; - its purpose is simply to clearly mark the control transfer point of the last CASE_OF (ie., after the last END_OF)
; - a difference: only one CASE_OF..END_OF structure is required (not minimum two)
; - a difference: an unlimited number of CASE_OF..END_OF structures allowed (not maximum ten)


CASE:      MACRO ?register
]CASINS$   = upper$("?register")      ; determine the comparison instruction to use
      .if ]CASINS$ == "ACCUM"
]CASINS$   = "CMP"
      .elseif ]CASINS$ == "X_REG"
]CASINS$   = "CPX"
      .elseif ]CASINS$ == "Y_REG"
]CASINS$   = "CPY"
      .else
      .fatal "Register name not recognized: ?register"
      .endif
      dyn.makelabel         ; make a label for END_CASE
]CASEND$   = ]DYNLBL$         ; save it for future reference
      ENDM
 ;----------------

CASE_OF:   MACRO ?value
      .putbacks ]CASINS$ " #?value"   ; write a compare instruction
      dyn.makelabel         ; make the next "no match" label
      dyn.ins.bra BNE       ; write a forward branch to it
      dyn.pushlabel         ; save it for future reference
      ENDM
 ;----------------

END_OF:      MACRO
]DYNLBL$   = ]CASEND$         ; get end of case structure label back
      dyn.ins.bra JMP       ; write a forward branch to it
      dyn.poplabel         ; get the previous "no match" label back
      dyn.ins.labelhere      ; mark this spot
      ENDM
 ;----------------

CASE_ELSE   MACRO            ; does nothing (optional, can be omitted)
      ENDM

 ;----------------

END_CASE:   MACRO
]DYNLBL$   = ]CASEND$         ; get end of case structure label back
      dyn.ins.labelhere      ; mark this spot
      ENDM

; -------------------------------

;                        +----------------+
;                        |   FOR...NEXT   |
;                        +----------------+

 ; FOR...NEXT below is more of a suggestion of one way to do it than anything.  As stated in the
 ; macros.html page, there are a lot of different possibilities to implement in a FOR...NEXT,
 ; and it's perhaps not possible to implement them all in the same macros.  This pair is for
 ; when the index begins and ends with constants, and it increments by 1 unless you put something
 ; between the FOR and NEXT to modify the index variable.

]FORLIM      = 0         ; save limit for NEXT macro (scalar, so FOR..NEXT cannot nest)

 ; example usage:
 ;
 ;      FOR  varname, 1, TO, 1000   ; (Unfotunately HXA macros require commas between the parameters)
 ;         <actions>
 ;         <actions>
 ;      NEXT varname

 ; NOTE:  FOR and NEXT will change the accumulator value (and the status).

FOR:      MACRO ?varname, ?index, ?to, ?limit
      LDA   #<(?index)   ; store the starting index value into the index variable
      STA   ?varname
      LDA   #>(?index)
      STA   1+?varname
      .if   "?to" != "TO"   ; a warning because this value doesn't really matter anyway
      .warn "Irregular syntax: ?to"
      .endif
]FORLIM      = ?limit      ; save limit value for NEXT
      dyn.makelabel      ; make a label for NEXT to come back to
      dyn.ins.labelhere   ; mark this spot
      dyn.pushlabel      ; save it for future reference
      ENDM
 ;----------------

NEXT:      MACRO ?varname
      INC   ?varname      ; increment the index variable by one
      BNE   +
      INC   1+?varname
 +
      dyn.poplabel      ; get FOR location back
      LDA   ?varname      ; low byte of index variable
      CMP   #<]FORLIM      ; matches low byte of limit ?
      dyn.ins.bra BNE      ; b:no - back to loop top
      LDA   1+?varname   ; same for high byte
      CMP   #>]FORLIM
      dyn.ins.bra BNE      ; watch the branch distance here !
      ENDM         ; drops through when loop complete
 ;----------------



Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Wed Jan 02, 2013 3:31 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
I am honored that someone would copy and promote them, transferring to another assembler.  You know how it is—you work hard at making it as concise as possible, ie, as clear as possible and as as short as you can without leaving out something important, and still wonder if anyone will take the time to read it and put it to use.

My main point was not necessarily that anyone had to use my own Forth-like structures, but to show that it can be done, and show one way it can be done even if the assembler was not written with this kind of thing in mind, and encourage the practice and suggest that other structures can be made on an as-needed basis.  But if it takes hold and people become acquainted enough with the structures that code examples posted on the forum can be made much more clear than much of what we've been seeing, so much the better.  Many code snippets posted are given only to show a concept, which they can more successfully accomplish using the structure macros without the reader necessarily having to run them if he doesn't have an equivalent set of macros for his assembler.

I just emailed Dan to ask him to add your assembler to his list of free 6502 assemblers at http://www.npsnet.com/danf/cbm/cross-de ... Assemblers, and told him about your structure macro support.  I'll put a note about it in my structure macros page too, and link it on my links page.  [Edit: done.]

When I get a little further along, I'm anxious to try the #v(x) feature of MPASM BitWise suggested for program-structure macros for PICs, as it is apparently a way to do indirect addressing in assembler variable arrays, allowing true stacks.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sat Feb 23, 2013 5:16 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
GARTHWILSON wrote:
teamtempest wrote:
Garth, I did not see any mention on your website, but have you considered that because your "CASE_OF" macro transfers control to "END_OF" when a comparison fails, any code placed between the last "END_OF" and "END_CASE" amounts to default processing? The above macro is essentially syntactic sugar to point out that's what's happening. I did the same thing in my version of your macros, except I called it "CASE_ELSE".

Yes, that is the intention.  In my web page on it, the example I gave was for outputting characters to a display, where in the case of $0A (LF), you do one thing, in the case of $0D (CR), you do another, and in the case of 08 (BkSpc) you do another, and if it's anything else, you do the default.  If you wish to clarify (although knowledge of the structure doesn't require it), you could have a do-nothing macro called CASE_ELSE or ELSE_OF or something like that.

I should also have mentioned that under certain circumstances, you may want some processing between one END_OF and the next CASE_OF comparison.  I'm sure the need would be rare, but I can imagine it.  [Edit, the following year:  I did need that on a work project.]

If there's a do-nothing CASE_ELSE though, I would like to see a matching END_ELSE or something like that so that the sequence of indentations appears symmetrical, easier to see at a glance that structures are finished and complete.  Rather than using the CASE_ELSE though, I prefer prefer to have the level of indentation give away what's happening.  (I think I have used ( ELSE_OF) in Forth though, which the compiler ignores because of the parentheses.) On the web page, I made each level of indentation three spaces, but four or even five might be more appropriate sometimes.  I also put two blank lines between END_OF and the next CASE_OF, which won't necessarily always be the perfect amount either.  Juggle it for best visual factoring.  That's what the macros are about anyway.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sun Feb 24, 2013 7:10 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 384
Location: Minnesota
Quote:
under certain circumstances, you may want some processing between one END_OF and the next CASE_OF comparison. I'm sure the need would be rare, but I can imagine it.


Mebbe so, but wouldn't that style of programming violate the point of trying to impose structure in the first place? It's easy enough to do that sort of thing in assembly or BASIC, but no self-respecting "structured" language would allow it. That way lieth spaghetti.

The original CASE macros create a visual structure like this:

Code:
CASE (..)
   CASE_OF
   ...
   END_OF
   CASE_OF
   ...
   END_OF
END_CASE


with a control transfer structure like this:

Code:
CASE
|->CASE_OF
|  ...
|  END_OF---|
|->CASE_OF  |
|  ...      |
|  END_OF---|
|->         |
END_CASE<---|


That is, CASE transfers control (by falling through) to CASE_OF, which (if the test fails) transfers control to just past the next END_OF. END_OF always unconditionally transfers control to END_CASE.

Thus the last CASE_OF transfers a failed test to just after the last END_OF, making any code there the default handler. If there is no code there, the last END_OF writes an unnecessary branch.

As it is there is nothing beyond typography, such as indentation, to mark a default handler. Someone who knows how the macros work would have no trouble reading it, but not everyone who tries to work out the code just from the visual structure may understand it. All a CASE_ELSE does is mark an explicit transfer point. If the symmetry of a matching (and similarly do-nothing) END_ELSE would help the goal of making the structure explicit, I don't have any objection.

I would like to mention that, like the FOR..NEXT macros, there are a variety of ways to write a CASE..ENDCASE macro. It is possible to write a version that transfers control from one CASE_OF directly to the next following CASE_OF, CASE_ELSE or END_CASE. One way to do it in HXA (I gotta go with what I'm familiar with!) is to have CASE create and save a label. CASE_OF pops a label, uses it to mark its position, writes its test, creates a new label, writes a jump to the new label for a failed test, and saves it. Each following CASE_OF does the same, creating a chain. CASE_ELSE is similar but does not write a test or a jump. END_CASE only pops a label and marks its own position, thus cleaning the stack and ending the chain.

Written this way, there are no extra jumps. It is also not possible to write code "in between" any branch because there is no "in between". The label CASE creates will never be referenced, but it makes writing CASE_OF simpler. A label created by CASE_ELSE will not be referenced either, but it makes writing END_CASE simpler. END_OF, if it exists at all, is a do-nothing macro that simply marks the end of a branch.


Top
 Profile  
Reply with quote  
 Post subject: Re: ANN: HXA0.190
PostPosted: Sun Feb 24, 2013 8:10 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8412
Location: Southern California
Quote:
Mebbe so, but wouldn't that style of programming violate the point of trying to impose structure in the first place?

What I'm thinking of is something like this:
Code:
    process process
    process process
    CASE
        CASE_OF A
            bla bla bla
            bla bla bla
        END_OF


        CASE_OF B
            bla bla bla
            bla bla bla
        END_OF


        CASE_OF C
            bla bla bla
            bla bla bla
        END_OF


        If_we_got_this_far_without_a_match_yet,_do
        some_other_processing_before_continuing.__Then:


        CASE_OF D
            bla bla bla
            bla bla bla
        END_OF


        CASE_OF E
            bla bla bla
            bla bla bla
        END_OF


        If_no_match_so_far,_do_this
        other_stuff_as_a_default.


    END_CASE
    next_instructions

If an A, B, or C match was found, the rest of the statement gets skipped, and there may be times that doing it this way works better than trying to split it up.  The interlude code must of course appropriately preserve or modify the thing we are testing so it is not lost when we continue with later CASE_OF's.  In the case of Forth, the item being tested is on the data stack, easily modified, or effortlessly preserved in the interlude code, which, BTW, could also be enclosed in do-nothing words similar to CASE_ELSE and END_ELSE, although I don't think it's necessary.  It's up to the individual.

I saw an old article this week on a BEGIN...WHILE...WHILE...REPEAT loop, ie, where the loop has multiple conditional-exit points.  It's something I might try to do in macros eventually, but the need seems rather rare, and I might initially choose to make the loop its own routine with multiple RTS's assembled by macros like RETURN_IF_EQ, RETURN_IF_NEG, etc..  It still qualifies since there are no branch or jump instructions, and it's clear without labels, just a routine name.  There's also a BEGIN...WHILE...UNTIL...THEN, where if the WHILE is not met, execution is resumed at the THEN, skipping the part immediately after the UNTIL.  Again, probably a rare need; but once the programmer understands how to do these things, (s)he can make or modify macros on an as-needed basis, and keep them for later re-use.

There is no intention of course to make anyone do their macros my way (which are heavily influenced by Forth), but rather to encourage others to take full advantage of their assemblers' macro capabilities to make structures to achieve less-cryptic code which accomplishes much of its purpose through visual factoring instead of the labels and branches which we have probably all printed out so we could draw the long branch arrows all over the paper to try to get a handle on a confusing piece of assembly-language code.  I've done it countless times myself.  Structure macros to the rescue!  :arrow:

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: