6502.org Forum  Projects  Code  Resources  Tools  Forum
It is currently Thu Nov 27, 2014 5:10 am

All times are UTC




Post new topic Reply to topic  [ 25 posts ]  Go to page Previous  1, 2
Author Message
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 12:36 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 252
Location: Minnesota
I think I see your point regarding "interbranch" processing. Still, to properly understand the control flow in your example (which is very nice) the reader has to know that END_OF terminates processing of the entire case statement (ala Pascal).

Suppose instead the reader assumes END_OF simply marks the termination of one branch (ala C). Under that interpretation both "interbranch" sequences are always executed, plus possibly one of the CASE_OF..END_OF branches if the condition A-E is ever fulfilled.

That interpretation might be avoided if it were possible to write 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

        CASE_ELSE
            bla bla bla
            bla bla bla

            CASE           
                CASE_OF D
                    bla bla bla
                    bla bla bla
                END_OF

                CASE_OF E
                    bla bla bla
                    bla bla bla
                END_OF

                CASE_ELSE
                    bla bla bla
                    bla bla bla
                END_ELSE

             END_CASE

        END_ELSE

    END_CASE
    next_instructions


With this visual structure it seems very clear that extra processing happens only if A-C doesn't happen, and default processing happens only if A-E doesn't happen. It does require CASE nesting. I can dimly see a way to make that happen. I'll give it some more thought.


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 3:51 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 3752
Location: Southern California
Quote:
It does require CASE nesting. I can dimly see a way to make that happen.

My ideas for the structures came mostly from Forth which allows endless nesting as long as you don't run out of data stack space during compilation (which would be hard to do on a 6502). It also adds "compiler security" which stops you if you have unmatched pairs, for example an OF...END_OF interrupted by an IF in an incomplete IF structure. That requires stack space too, where the OF puts a compiler-security number on the stack, and the END_OF compares to make sure it's the right one. If it sees the one for IF instead, it gives the error message.

What I did in the assembler macros mimics what I did in my '816 Forth kernel except that the CASE statement in macros uses its own variables instead of taking a ton of assembler stack space, which I figured would be fine since I've never needed to nest CASE statements. I did not go to the extent of the "compiler security" in my macros (would that be called "assembler security"? hmmm...maybe it needs a better name), but so far I have not had any errors in that department, mostly I suppose because the indentation keeps them straight, and because the assembler prohibits having more than one instruction or macro per line anyway.

What you could do if you want to nest CASE structures is to have CASE put a number on the assembler stack which is the initialized number of cases, and then each case that is encountered increments that number. This number keeps getting moved to the top of the stack so that END_CASE knows how deep to go in the stack as it collects addresses to fill in with the end-of-structure address for each END_OF to jump to.

It has been argued that the internals of the CASE structure could use a mass of nested IF structures, which is true, but doing it that way in Forth it's not as efficient in either speed or memory as having true CASE-structure internals. But for assembly, I can't think of any differences right now.

Other accompanying words you might be interested in are RANGE_OF and SET_OF. I haven't done them in macros yet since that would really escalate the internal complexity and they're not often needed, but here's an example of the usage: Suppose you have various cases that are an individual number, but then one case is a range of numbers, and another case is a special set of numbers that don't particularly meet a function you could spell out; so you have for example:
Code:
    CASE
        CASE_OF  1
            bla bla bla
            bla bla bla
        END_OF

        CASE_OF  2
            bla bla bla
            bla bla bla
        END_OF

        RANGE_OF  3 to 11
            bla bla bla
            bla bla bla
        END_OF

        SET_OF  {13, 16, 20, 25, 33, 151}        ; The list can be constants, expressions to be evaluated at assembly time, etc..
            bla bla bla
            bla bla bla
        END_OF

        default actions used for
        any case not specified above
    END_CASE

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 8:37 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 3222
Location: England
FWIW, I like this CASE_ELSE - it needn't be do-nothing, depending on how sophisticated the assembler chooses to be. With CASE_ELSE, unadorned code inside a CASE-END_CASE are errors (or at least warnings)

One little drawback of a macro-heavy style is that it's not directly usable when posted here (for example) - it serves only as pseudocode, because the reader may not have the macros in question in their assembler of choice. Perhaps the answer is to post a listing, or hexdump, or plain code in addition to the structured code.

Cheers
Ed


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 9:13 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 3752
Location: Southern California
BigEd wrote:
FWIW, I like this CASE_ELSE - it needn't be do-nothing, depending on how sophisticated the assembler chooses to be. With CASE_ELSE, unadorned code inside a CASE-END_CASE are errors (or at least warnings)

Many assemblers allow you to generate your own error messages in conditional assembly, which could be used to warn of incomplete structures. I'm not sure that's what you're talking about though. Is it something different?

Quote:
One little drawback of a macro-heavy style is that it's not directly usable when posted here (for example) - it serves only as pseudocode, because the reader may not have the macros in question in their assembler of choice. Perhaps the answer is to post a listing, or hexdump, or plain code in addition to the structured code.

The article is at http://wilsonminesco.com/StructureMacros/index.html with a few specific examples of plain versus structured code and then non-specific examples of the rest of the structures (like the above with "bla bla bla" filling in), and the links to the .ASM code are at the very top of the page. Admittedly, you may need to make changes for your particular assembler. I found bugs in the C32 assembler when I did this, and my code shows what I did to get around them. None of the assemblers I've used actually came with any macros. I've always written all the macros I've ever used. My structure macros for PIC (for the MPASM assembler) are at http://wilsonminesco.com/StructureMacro ... ru_MAC.ASM. I will add to both when I finish the project I'm working on.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 9:52 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 3222
Location: England
Hi Garth
yes, I meant assembly-time warnings or errors in the case of "incorrect" structures.

I suppose my point about macros is that they may be invaluable for a 10,000 line program, but for a 10-line sketch of something which might otherwise be pasted into any assembler (including a mini-assembler with no macros, such as py65 or easy6502) they may be more of a barrier than a benefit. Bruce's marvellous posts would be less compelling if they could only be enjoyed after a meal of macros.

Cheers
Ed


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 10:04 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 3752
Location: Southern California
and that is what makes me kind of want to post both versions of every piece of code I show from now on (although if I publish something kind of large, I won't be taking the time to translate it).

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Mon Feb 25, 2013 10:28 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 3222
Location: England
That would be helpful! Whenever you show both versions, you are also demonstrating the value of the macros.


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Tue Feb 26, 2013 2:54 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 252
Location: Minnesota
Here's a first pass at set of nestable CASE..END_CASE macros. They are adapted versions of the ones I originally wrote to mimic Garth's originals.

This isn't the only way I can imagine to write them - I used one multi-purpose "stack" here, but three single-purpose "stacks" are another possible choice.

Code:
; Nestable 6502 CASE..ENDCASE Structure Macros

; for HXA 0.191 or Higher

; by Anton Treuenfels

; first created: 02/25/13
; last revised:  02/25/13

; INCLUDE this file to use these macros

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

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

   .readonce      ; prevent multiple inclusion

; -------------------------------
; "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 label storage

             ; 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 of HXA)

        .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.put.bra, ?mnemonic
   .putbacks "?mnemonic " ]DYNLBL$
   .endm

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

   .macro dyn.put.labelhere
   .putbacks ]DYNLBL$
   .endm

   ; push back (implied) 6502 compare instruction to input stream
   ; - "CMP/CPX/CPY #?val" will become the next input line

   .macro dyn.put.cmp, ?val
   .putbacks ]DYNLBL$ "?val"
   .endm

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

; - the CASE_OF "no match" branch leads to the next CASE_OF (to just after the "JMP" instruction it writes)
; - END_OF does nothing (but is allowed for compatibility with existing code)
; - a single (optional) CASE_ELSE may be used to explicitly declare a final default processing branch
; following all CASE_OF branches (without CASE_ELSE there will be no default processing)
; - since transfers to END_CASE can occur only at the start of branches,
; there will be no spurious "JMP" instruction at the end of the last branch
; - at least one CASE_OF..END_OF structure is required
; - an unlimited number of CASE_OF..END_OF structures is allowed

; CASE begins a CASE..ENDCASE structure
; - stacks three items (from stack top down):
; - a flag indicating a newly opened CASE..ENDCASE structure
; - a "no match" forward branch label
; - a compare instruction

CASE:      .macro ?register
]DYNLBL$   = upper$("?register")      ; validate register
      .if ]DYNLBL$ == "ACCUM"      ; if matched...
]DYNLBL$   = "CMP   #"         ; ...convert to instruction
      .elseif ]DYNLBL$ == "X_REG"
]DYNLBL$   = "CPX   #"         ; must be exactly DYNLEN characters long!
      .elseif ]DYNLBL$ == "Y_REG"
]DYNLBL$   = "CPY   #"
      .else
      .fatal "Register name not recognized: ?register"
      .endif
      dyn.pushlabel         ; save compare instruction
      dyn.makelabel         ; make "no match" label (will not be referenced)
      dyn.pushlabel         ; save it
]DYNLBL$   = "1STCASE"         ; flag: CASE..ENDCASE just opened
      dyn.pushlabel         ; save it (also DYNLEN characters long)
      .endm

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

; CASE_OF starts a branch
; - replaces top stack item with actual END_CASE label

; writes four lines of code (only three if first CASE_OF):

;           jmp END_CASE         ; previous branch complete - jump to end
; nomatch               ; "no match" label from previous branch
;      cmp/cpx/cpy ?val      ; is this the branch to take ?
;      bne CASE_OF/CASE_ELSE/END_CASE   ; no, so try next branch (or done)

CASE_OF:   .macro ?value
      dyn.poplabel         ; pop top stack entry
      .if ]DYNLBL$ == "1STCASE"   ; first CASE_OF?
      dyn.makelabel         ; make label for END_CASE
      .else            ; at least one preceeding CASE_OF
      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 ?value      ; write compare instruction
      dyn.pushlabel         ; save it for future reference
      dyn.makelabel         ; make the next "no match" label
      dyn.put.bra BNE       ; 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

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

END_OF:      .macro            ; does nothing (optional, can be omitted)
      .endm

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

; CASE_ELSE starts default processing branch

; writes two lines of code:

;           jmp END_CASE         ; previous branch complete - jump to end
; nomatch               ; "no match" label from previous branch

CASE_ELSE:   .macro
      dyn.poplabel         ; get back END_CASE label (terminate last CASE_OF)
      dyn.put.bra JMP       ; write branch to end of CASE structure
]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.makelabel         ; make a new "no match" label (will not be referenced)
      dyn.pushlabel         ; save it for END_CASE
]DYNLBL$   = ]DYNTMP$         ; get back END_CASE label
      dyn.pushlabel         ; save it for END_CASE
      .endm

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

END_ELSE:   .macro            ; does nothing (optional, can be omitted)
      .endm

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

; END_CASE marks end of CASE..END_CASE structure
; - cleans stack

; writes two lines of code:

; endcase               ; END_CASE label
; nomatch               ; "no match" label from last branch

END_CASE:   .macro
      dyn.poplabel         ; get back END_CASE label
      dyn.put.labelhere      ; mark this spot as its destination
      dyn.poplabel         ; get back last "no match" label
      dyn.put.labelhere      ; mark this spot as its destination
      dyn.poplabel         ; discard compare instruction
      .endm


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Wed Feb 27, 2013 12:27 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 252
Location: Minnesota
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!


Top
 Profile  
 
 Post subject: Re: ANN: HXA0.190
PostPosted: Sun May 12, 2013 3:32 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 3752
Location: Southern California
GARTHWILSON wrote:
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.

Done. The structure macros section of my website has been revised, with quite a bit added.

The FOR_X...NEXT_X and FOR_Y...NEXT_Y mentioned above are very flexible, with lots of options for how to use X or Y as an 8-bit counter, and nestable.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 25 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


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: