6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Apr 28, 2024 7:37 am

All times are UTC




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Sat Jul 07, 2012 10:49 pm 
Offline

Joined: Sat Jul 07, 2012 10:46 pm
Posts: 7
Is this possible?

For example if I define a byte variable and a macro like so? Thanks in advance!

Code:
.ENUM $80
      var1  db
.ENDE

.MACRO DoSomething
      lda \1   ; Result
.ENDM

DoSomething var1



Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2012 12:43 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
It doesn't make any sense in the assemblers I'm familiar with. What's the goal? If all you want to do is rename LDA (either ZP or abs) to "DoSomething", you would usually have something like:
Code:
DoSomething: MACRO addr
             LDA   addr
             ENDM

It would be kind of pointless though.

Defining the byte variable in the C32 assembler would be something like:
Code:
var1:   DFS  1
where "DFS" stands for "DeFine data Storage," and the "1" tells how many bytes to assign to it. In the 2500AD assembler, it would be BLKB for "define a BLocK of Bytes" instead of DFS.

Then if you wanted to invoke the macro with that variable:
Code:
        DoSomething  var1

One of our earlier macro topics is at viewtopic.php?f=2&t=1586 .

_________________
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  
PostPosted: Sun Jul 08, 2012 1:35 am 
Offline

Joined: Sat Jul 07, 2012 10:46 pm
Posts: 7
My example is simple but there is a reason for my wanting to do this.

Let's say I wanted to execute a macro 1000 times. The macro needs an index variable. I would much rather have 1 variable and inc it's value and then pass it to the macro as an argument vs having an extremely large code block.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2012 3:30 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
You could make macros that do something like FOR...NEXT in BASIC, like the following. (I have not tried them but they should give the idea even if I forgot something.)
Code:
var1:     DFS   2   ; 2-byte variable for the target computer to keep the loop index
TO:       EQU   0   ; (really just a dummy value for the FOR...NEXT macros.  Value not used.)
FOR_LIM:  SETL  0   ; assembler variable used to store the limit to build the CMP# instructions in NEXT
FOR_ADR:  SETL  0   ; assembler variable used to record the loop beginning addr, to form the branch instruction in NEXT


FOR:      MACRO  var_name  index  dummy  limit
          LDA   #<index
          STA   var_name    ; store the low byte of the starting index
          LDA   #>index
          STA   var_name+1  ; store the high byte of the starting index
 FOR_LIM: SETL  limit       ; Store the limit in an assembler variable so NEXT knows what to compare to.
 FOR_ADR: SETL  $           ; Store the address of the top of the loop in an assembler variable so NEXT
          ENDM              ; knows where to branch back up to if the looping is not finished.
 ;--------------------

NEXT:     MACRO  var_name
          INC  var_name     ; Increment the low byte of the index,
          BNE  nx1          ; and, if that didn't make it roll over to $00,
          INC  var_name+1   ; increment the high byte too.

 nx1:     LDA  var_name     ; Get the index low byte and
          CMP  #<FOR_LIM+1  ; compare it to the low byte of the limit plus one.
          BNE  FOR_ADR      ; If they're different, go back to the top of the loop.
          LDA  var_name+1   ; Otherwise, get the index high byte and
          CMP  #>FOR_LIM+1  ; compare it to the high byte of the limit plus one.
          BNE  FOR_ADR      ; If it doesn't match, branch back up to the top of the loop.
          ENDM              ; Otherwise you're done, so drop through.
 ;--------------------

Now you could have the loop like this, no longer having to look at the internal details of the control:
Code:
          FOR var1 1 TO 1000
              <instructions>
              <instructions>
          NEXT var1

There are lots of ways it could be done. In the case of 1000, you could have two nested loops of for example 250 (which fits in the 0-255 range of one byte) and 4, which would be more efficient; but it would not allow values that aren't the exact product of two single-byte values. FOR and NEXT above leave index registers X and Y available for other uses.

This is one thing where the 65816 makes the code a lot shorter, because you can have 16-bit index registers:
Code:
label:  LDX  #1000
           <instructions>
           <instructions>
           DEX
        BNE  label

(You might still want structure macros to do this though, so you can get rid of the labels and still shorten it by a line or two while making it even more clear.)

The FOR...NEXT loop could be modified to take a STEP size too, and to have a way to leave the loop early if conditions dictate. If you want to be able to nest them, you will probably have to make separate ones, with the second set having a . or _ after the macro name or something to distinguish them. I wish assemblers allowed assembler arrays and a way to index into them, so a stack could be synthesized, allowing unlimited nesting of program structures. So far I have not seen an assembler that allows that. Edit, 7/28/12: I figured out how to do it, and wrote it up at http://wilsonminesco.com/StructureMacros/index.html . There are links to working source code there too.

I'm writing an article on using assembler macros to make program structures. I had not decided on a FOR...NEXT type of structure yet though, so this is not from what I'm working on. What I do have in the works so far is BEGIN...AGAIN, BEGIN...WHILE...REPEAT, BEGIN...UNTIL, IF...ELSE...END_IF, and the CASE statement. Edit, 7/28/12: Done. http://wilsonminesco.com/StructureMacros/index.html .

_________________
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  
PostPosted: Sun Jul 08, 2012 4:45 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Hi akaitora
Welcome!

You certainly can use parameters in the assemblers I'm familiar with. But note that different assemblers have different syntax. Which assembler are you using?

In your example you use \1 - I'm familiar with backslashes for parameter expansion in gcc's assembler, but one still uses a named parameter:
Code:
        .macro  sum from=0, to=5
        .long   \from
        .if     \to-\from
        sum     "(\from+1)",\to
        .endif
        .endm


In ca65 one just uses the name:

Code:
.macro  inc16   addr
                clc
                lda     addr
                adc     #$01
                sta     addr
                lda     addr+1
                adc     #$00
                sta     addr+1
.endmacro


BDD has written a collection of macros for the Kowalski development environment which includes this
Code:
cop      .macro .op            ;co-processor
         .if .op > $ff
             .error "SIGNATURE MUST BE $00 - $FF"
         .endif
         .byte $02,.op
         .endm
;

Where again he names the parameter but uses a leading dot in the name and the use. I don't know if that is convention or necessity.

Hope this helps

Cheers
Ed


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2012 10:41 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
GARTHWILSON wrote:
I'm writing an article on using assembler macros to make program structures. I had not decided on a FOR...NEXT type of structure yet though, so this is not from what I'm working on. What I do have in the works so far is BEGIN...AGAIN, BEGIN...WHILE...REPEAT, BEGIN...UNTIL, IF...ELSE...END_IF, and the CASE statement.

My assembler supports structured assembly directly without macros in both 6502 and 65816 mode.

Look at the bottom of http://www.obelisk.demon.co.uk/dev65/as65.html

_________________
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  
PostPosted: Sun Jul 08, 2012 1:44 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Quote:
My example is simple but there is a reason for my wanting to do this.

Let's say I wanted to execute a macro 1000 times. The macro needs an index variable. I would much rather have 1 variable and inc it's value and then pass it to the macro as an argument vs having an extremely large code block.


Often it helps others to know "what is the problem you're really trying to solve?" rather than "what's the technique for doing 'X'?" The latter only implies that you've thought of a solution to a problem but simply don't know how to make it work. But perhaps there are other solutions to the real problem you haven't thought of yet.

Your description of the "real problem" above still isn't completely clear to me. Are you to trying to expand a macro 1000 times at assembly time? If so, that's still a lot of code no matter what variable you use. Or are you at run time trying to execute the code of one macro expansion 1000 times? If so, why a macro and not a subroutine?

As far as I understand your original question, the answer is "yes". The most basic function of all macro processors is simple text substitution. The form you show is what I'd call an "implied" form, where any actual arguments provided to the macro at expansion time are assigned to pre-defined formal argument names (typically numbered names, often with the name '0' standing for the total number of arguments supplied). It's possibly the most elementary form (ie., the easiest to implement) of macro processor. Not as easy to use as those that let you name formal arguments yourself, though.

In the 6502-family world at least the Merlin series of assemblers use this form of macro. I don't recall any others offhand, but there certainly may be.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2012 1:58 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Quote:
I wish assemblers allowed assembler arrays and a way to index into them, so a stack could be synthesized, allowing unlimited nesting of program structures. So far I have not seen an assembler that allows that.


Aw, there are other ways of achieving the same thing. What you need is just something - anything - that you can use to reliably put one thing on and then take it off again. Here's an approach that uses strings to create "IF..ENDIF" and "IF..ELSE..ENDIF" structures:

http://home.earthlink.net/~hxa/demo/demo050.a


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2012 9:07 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8147
Location: Midwestern USA
BigEd wrote:
BDD has written a collection of macros for the Kowalski development environment which includes this
Code:
cop      .macro .op            ;co-processor
         .if .op > $ff
             .error "SIGNATURE MUST BE $00 - $FF"
         .endif
         .byte $02,.op
         .endm
;

Where again he names the parameter but uses a leading dot in the name and the use. I don't know if that is convention or necessity.

In the Kowalski simulator's assembler, a label preceded with a dot is a "local label," and thus has a limited scope. When used within a macro definition, a local label's scope is bounded by the .MACRO and .ENDM pseudo-ops, unless a global label is also defined within the macro code (not a good practice). Outside of a macro, a local label's scope is bounded by the two nearest global labels. Example code taken from POC V1's BIOS:

Code:
;===============================================================================
;
;PRINT NULL-TERMINATED CHARACTER STRING — 32K chars maximum
;
;   ————————————————————————————————————————————————————————
;   Preparatory Ops : .X: string address LSB
;                     .Y: string address MSB
;
;   Register Returns: .A: used
;                     .B: used
;                     .X: used
;                     .Y: used
;
;   MPU Flags: NVmxDIZC
;              ||||||||
;              |||||||+———> 0: okay
;              |||||||      1: string too long (1)
;              ||||||+————> not defined
;              ||||++—————> not defined
;              |||+———————> 1
;              ||+————————> 1
;              |+—————————> not defined
;              +——————————> not defined
;
;   Example: LDX #<STRING
;            LDY #>STRING
;            JSR SPRINT
;            BCS TOOLONG
;
;   Notes: 1) Maximum permissible string length including the
;             terminator is 32,768 bytes.
;          2) All registers are forced to 8 bits.
;   ————————————————————————————————————————————————————————
;
sprint   shorta                ;8 bit registers
         phy                   ;save string pointer MSB
         phx                   ;save string pointer LSB
;————————————————————————————————————————————————————————
.sptr    =1                    ;string pointer stack index
;————————————————————————————————————————————————————————
         longx                 ;16 bit .X & .Y
         clc                   ;no initial error
         ldy #0                ;starting index
;
.0000010 lda (.sptr,s),y       ;get character
         beq .0000020          ;EOS, done
;
         jsr bsout             ;print char
         iny
         bpl .0000010          ;next
;
         sec                   ;string too long — error
;
.0000020 plx                   ;clean up stack
         ply
         rts
;
;===============================================================================
;
;GET CONSOLE INPUT
;
input    shortx                ;8 bit registers
;
         ...etc...

In the above example, the scope of the local labels .SPTR, .0000010 and .0000020 starts with SPRINT and ends at INPUT. All three labels may be reused anywhere else outside of SPRINT. This basic principle exists in most assemblers that support local labels.

Incidentally, the macro I use to call SPRINT is:

Code:
printstr .macro .s             ;print a string
         ldx #<.s
         ldy #>.s
         jsr sprint
         .endm

Here, the local label .S is bounded by the .MACRO and .ENDM pseudo-ops and thus could be reused elsewhere in the source code. The macro's usage is:

Code:
         printstr txtstrng

While bloviating about labels and such, the Kowalski assembler also supports "variable" labels. For example:

Code:
;————————————————————————————————————————————————————————
.sptr    .set 1                ;intial string pointer stack index, but can be changed
;————————————————————————————————————————————————————————
         pha                   ;push some value
;————————————————————————————————————————————————————————
.sptr    .set .sptr+1          ;change stack index to reflect effect of PHA on stack pointer
;————————————————————————————————————————————————————————
         ...execute code, then...
         pla                   ;pull previous value
;————————————————————————————————————————————————————————
.sptr    .set .sptr-1          ;restore previous stack index to reflect effect of PLA on stack pointer
;————————————————————————————————————————————————————————
         ...etc...

In the above, I change .SPTR's value according to what is going on with the stack by using the .SET pseudo-op. Although this example is with a local label, .SET may also be applied to a global label, but is not a particularly good idea. .SET is an assembly-time operation and its effects are global if applied to a global label. Such an operation could conceivably result in a phase error.

I get a lot of use from .SET, especially in defining stack frames that may vary in structure as a subroutine executes. Without it, keeping track of a large stack frame would become quite tedious and error-prone.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 09, 2012 3:30 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
BitWise wrote:
GARTHWILSON wrote:
I'm writing an article on using assembler macros to make program structures. I had not decided on a FOR...NEXT type of structure yet though, so this is not from what I'm working on. What I do have in the works so far is BEGIN...AGAIN, BEGIN...WHILE...REPEAT, BEGIN...UNTIL, IF...ELSE...END_IF, and the CASE statement.

My assembler supports structured assembly directly without macros in both 6502 and 65816 mode.

Look at the bottom of http://www.obelisk.demon.co.uk/dev65/as65.html

I think I'd have to say it is macros—just built in.

What you have appears to be exactly what I've been wanting. Is it nestable? (I expect and hope it is.) I might still continue the work though, to show how to do the same thing with other assemblers, including non-65-family ones.

teamtempest wrote:
Quote:
I wish assemblers allowed assembler arrays and a way to index into them, so a stack could be synthesized, allowing unlimited nesting of program structures. So far I have not seen an assembler that allows that.

Aw, there are other ways of achieving the same thing. What you need is just something - anything - that you can use to reliably put one thing on and then take it off again. Here's an approach that uses strings to create "IF..ENDIF" and "IF..ELSE..ENDIF" structures:

http://home.earthlink.net/~hxa/demo/demo050.a

I'm not sure I understand what he's doing there, but it does give me an idea of another way to do it.

Thankyou both for the links.

_________________
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  
PostPosted: Mon Jul 09, 2012 7:46 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
GARTHWILSON wrote:
BitWise wrote:
GARTHWILSON wrote:
I'm writing an article on using assembler macros to make program structures. I had not decided on a FOR...NEXT type of structure yet though, so this is not from what I'm working on. What I do have in the works so far is BEGIN...AGAIN, BEGIN...WHILE...REPEAT, BEGIN...UNTIL, IF...ELSE...END_IF, and the CASE statement.

My assembler supports structured assembly directly without macros in both 6502 and 65816 mode.

Look at the bottom of http://www.obelisk.demon.co.uk/dev65/as65.html

I think I'd have to say it is macros-- just built in.

What you have appears to be exactly what I've been wanting. Is it nestable? (I expect and hope it is.) I might still continue the work though, to show how to do the same thing with other assemblers, including non-65-family ones.

Its a bit more than macros. It generates different code sequences to take advantage of BRA when its available, uses branches when the target is in range and jumps when it is not.

The constructs are fully nestable.

_________________
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  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 21 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: