BeebASM assigning variables in macros

handy tools that can assist in the development of new software
Post Reply
jamesp
Posts: 8
Joined: Tue Dec 07, 2021 3:08 am
Contact:

BeebASM assigning variables in macros

Post by jamesp »

I've been using BeebASM and have been defining collections of variables for the addresses of things such as 16 bit values or VIA registers. These typically have a common prefix, e.g:

Code: Select all

ZP_COUNTER_L = &70
ZP_COUNTER_H = &71
Its quite tedious defining this sort of thing so what I initially tried to do was to use a MACRO to make defining things like that more concise, however it's not possible to create a variable whose name is dynamically generated from an expression. I've got a proposed extension to BeebASM, adding a new directive ASSIGN:

Code: Select all

ASSIGN "<variable-name>", <value>
Using this, it is possible to define a MACROs such as:

Code: Select all

MACRO SETW name, addr
  ASSIGN name, addr
  ASSIGN name + "_L", addr
  ASSIGN name + "_H", addr+1
ENDMACRO

SETW "ZP_COUNTER", &70

.reset
    stz     ZP_COUNTER_L
    stz     ZP_COUNTER_H
    rts
I've implemented this as an experiment and am wondering if there is wider interest in it and whether the general approach acceptable. If so I can look at cleaning this up and raising a pull request.
tnash
Posts: 161
Joined: Mon May 02, 2022 9:56 am
Contact:

Re: BeebASM assigning variables in macros

Post by tnash »

I'm currently using a spreadsheet for the same purpose so yes this would be nice
User avatar
hjalfi
Posts: 175
Joined: Sat May 13, 2017 11:17 pm
Location: Zürich, Switzelrand
Contact:

Re: BeebASM assigning variables in macros

Post by hjalfi »

There is an ASM <some string> keyword, but it appears that this doesn't work for anything which isn't a simple instruction.
David Given
http://cowlark.com
jamesp
Posts: 8
Joined: Tue Dec 07, 2021 3:08 am
Contact:

Re: BeebASM assigning variables in macros

Post by jamesp »

One thing I've noticed is that it doesn't follow the same scoping rules as normal variable definitions. While this is a good thing for breaking the variables out of the MACRO, it was more luck than judgement and I need to go figured out exactly why this worked.

The other thing I attempted was creating a MACRO which in effect had its own auto-incrementing counter address, so defining the contents of a workspace could look something like:

Code: Select all

WORKSPACE "ZP", &70 
SETW "ZP", "COUNTER1"    ; ZP_COUNTER_1 = &70
SETW "ZP", "COUNTER2"    ; ZP_COUNTER_2 = &72
SETB "ZP", "A"           ; ZP_A = &74 
SETB "ZP", "B"           ; ZP_B = &75
This means I can relocate the definitions to the files using them, while making it easy to make changes to the workspace. I'm not sure how useful this sort of thing is in general though, I'm playing around with a bare bone 6502, not a Beeb sideways ROM.

I think all that's necessary here would be to extend ASSIGN be able to re-assign to a variable to maintain the address, a bit like using the the `=?` syntax. Not sure if this is a good idea yet, but perhaps:

Code: Select all

ASSIGN? "<name>", <value>
jamesp
Posts: 8
Joined: Tue Dec 07, 2021 3:08 am
Contact:

Re: BeebASM assigning variables in macros

Post by jamesp »

Or maybe DEFINE which won't overwrite an existing variable and ASSIGN which can?
jamesp
Posts: 8
Joined: Tue Dec 07, 2021 3:08 am
Contact:

Re: BeebASM assigning variables in macros

Post by jamesp »

I think I have something quite useful now. There are now two directives: DEFINE which can create new variables, but not change them; and ASSIGN which can also change them.

The ability to update a variable makes it possible to define macros which can workspace to be automatically allocated, e.g. using the macros attached below:

Code: Select all

; Define a named region of workspace. WSET... macros ensure it doesn't overflow.
WDEFINE "ZP" , &0000, &0100 

; Allocate address in the "ZP" workspace, these can be located anywhere before they are
; used, they don't need to be in a single block which is nice when you split things into different
; files.
WSETW   ZP, "r0"                  ; Scratch space
WSETW   ZP, "r1"                  ;  ...
WSETW   ZP, "r2"                  ;  ...
WSETW   ZP, "irq_r0"              ; Scratch space for IRQ handlers.
WSETW   ZP, "irq_r1"              ;  ...
WSKIPTO ZP, &FC
WSETB   ZP, "irq_a"               ; IRQ handler stores A here [MOS &FC].

; Can then reference those by name, e.g:
lda irq_a

; Multibyte values also have variables for each component, e.g: 
clc
lda r1_l
adc r0_l
sta r1_l
lda r1_h
adc r0_h
sta r1_h
Can dump the assignments by adding -D DUMP_WS=1 to the beebasm command line, which spits out something like:

Code: Select all

r0                  = &0000 +2
r1                  = &0002 +2
r2                  = &0004 +2
irq_r0              = &0006 +2
irq_r1              = &0008 +2
irq_a               = &00FC +1
I've attached the macros I've been experimenting with using this feature:
macros.6502.txt
Example macros using experimental DEFINE and ASSIGN directives
(3.49 KiB) Downloaded 22 times
If anyone wants a look there is a pull request here: https://github.com/stardot/beebasm/pull/95
User avatar
ctr
Posts: 259
Joined: Wed Jul 16, 2014 3:53 pm
Contact:

Re: BeebASM assigning variables in macros

Post by ctr »

DEFINE looks useful, though it's not obvious from the readme that it sets global variables. It could be renamed to GLOBAL to emphasise the point?

I'm probably teaching you to suck eggs, but a common way to define variables is to use labels, for example:

Code: Select all

ORG &70
.ptr
    EQUW 0
.bvar1
    EQUB 0
.bvar2
    EQUB 0
This avoids having to explicitly set the location of every variable but it does separate the definitions from their use. On the other hand, when memory is tight it can be useful to get an overview of memory usage.

Anyway, by design, it has never been possible to change the value of a symbol in beebasm. Doing so has some unexpected consequences. For example, this prints 2 twice:

Code: Select all

c=1
PRINT c
ASSIGN "c",2
PRINT c
So I'm not keen on ASSIGN. This feature has been requested before and allocating memory is nearly always the motivation. Another option is to add an explicit allocation feature. Perhaps some way of defining named regions and allocating from them. This would integrate with the existing code that checks that code blocks don't inadvertently overlap. Maybe something like:

Code: Select all

REGION zp, &70, &90
REGION code, &1900 \ End address is optional
ORG zp      \ Allocate a variable in zero page
.var
    EQUB 0
ORG code    \ Assemble some code
.start
LDA var
RTS
ORG zp      \ Allocate another variable
.another
    EQUW 0
This is quite simple and seems like it might be useful.
User avatar
lovebug
Posts: 1739
Joined: Sun Jan 31, 2021 5:07 pm
Location: Magrathea
Contact:

Re: BeebASM assigning variables in macros

Post by lovebug »

This is something I've used in other assemblers and is a feature I would like to see in beebasm and was contemplating writing it myself

Are the source code changes for this available ?

Thanks
Image Image Image Image
User avatar
ctr
Posts: 259
Joined: Wed Jul 16, 2014 3:53 pm
Contact:

Re: BeebASM assigning variables in macros

Post by ctr »

lovebug wrote: Thu Nov 02, 2023 9:15 pm Are the source code changes for this available ?
Which one? James's code is on github. There's a link at the end of his last comment. Mine is just a proposal. I was hoping for some feedback before implementing it.
VectorEyes
Posts: 572
Joined: Fri Apr 13, 2018 2:48 pm
Contact:

Re: BeebASM assigning variables in macros

Post by VectorEyes »

ctr wrote: Thu Nov 02, 2023 8:26 pm This would integrate with the existing code that checks that code blocks don't inadvertently overlap. Maybe something like:

Code: Select all

REGION zp, &70, &90
REGION code, &1900 \ End address is optional
ORG zp      \ Allocate a variable in zero page
.var
    EQUB 0
ORG code    \ Assemble some code
.start
LDA var
RTS
ORG zp      \ Allocate another variable
.another
    EQUW 0
This is quite simple and seems like it might be useful.
Interesting idea but I’m not keen on that syntax. There are two “ORG zp” statements that set the compilation address to different values! That’s not how ORG works right now. I know that’s because “zp” in this case is a “region” but it seems like a recipe for confusion. Possibly better to have an “ORG_IN_REGION” keyword. To be honest, I’m not really seeing the benefit of your proposal as opposed to just declaring .var and .another in one block, one after another. It’s not that much of a faff to structure your declarations like that.
User avatar
ctr
Posts: 259
Joined: Wed Jul 16, 2014 3:53 pm
Contact:

Re: BeebASM assigning variables in macros

Post by ctr »

VectorEyes wrote: Thu Nov 02, 2023 11:20 pm Interesting idea but I’m not keen on that syntax. There are two “ORG zp” statements that set the compilation address to different values! That’s not how ORG works right now. I know that’s because “zp” in this case is a “region” but it seems like a recipe for confusion. Possibly better to have an “ORG_IN_REGION” keyword.
You're right, it's easier to write than to read. I'm not keen on ORG_IN_REGION but it needs to be different.
VectorEyes wrote: Thu Nov 02, 2023 11:20 pm To be honest, I’m not really seeing the benefit of your proposal as opposed to just declaring .var and .another in one block, one after another. It’s not that much of a faff to structure your declarations like that.
That's where I am. But there has been some demand over the years for a feature that lets you put your variable declarations in separate files along with the code that uses them. The obvious way to support this is with symbol reassignment but this really isn't a good idea (as I explain below). My aim was to come up with something minimal that would satisfy the demand. But if there isn't much interest I probably won't do it.

On reassignment: during the first pass of assembly beebasm memoises the value of every symbol, including the values of every instance of every local variable. During the second pass beebasm just reuses the values. This ensures consistency between the passes. If you start changing the values of symbols this model breaks.
User avatar
sweh
Posts: 3314
Joined: Sat Mar 10, 2012 12:05 pm
Location: 07410 New Jersey
Contact:

Re: BeebASM assigning variables in macros

Post by sweh »

ctr wrote: Fri Nov 03, 2023 1:51 pm You're right, it's easier to write than to read. I'm not keen on ORG_IN_REGION but it needs to be different.
How about "VARSET"; a variable set...
Rgds
Stephen
User avatar
ctr
Posts: 259
Joined: Wed Jul 16, 2014 3:53 pm
Contact:

Re: BeebASM assigning variables in macros

Post by ctr »

sweh wrote: Fri Nov 03, 2023 3:08 pm How about "VARSET"; a variable set...
It's equally useful for code regions, like a section in a traditional linker. "Section" might be a better name than "region".
tom_seddon
Posts: 889
Joined: Tue Aug 30, 2005 12:42 am
Contact:

Re: BeebASM assigning variables in macros

Post by tom_seddon »

64tass lets you define things in sections, which can be instantiated as necessary, possibly out of order. This works pretty well for allocating memory. As an example, you can define some zero page for one routine somewhere, probably near the code for the routine itself:

Code: Select all

    .section routine_1_zp
src: .fill 2
dest: .fill 2
    .endsection
And then some zero page for another one, anywhere else in the code that makes sense:

Code: Select all

    .section routine_2_zp
count: .fill 1
reason: .fill 1
    .endsection
Then the decision about which addresses they live at can be taken elsewhere:

Code: Select all

*=$a8
    .dsection routine_1_zp
    .dsection routine_2_zp
64tass does as many passes as required for things to settle down, so you can do all of this textually in pretty much any order.

This feature seems designed primarily for code and initialized data, but with a bit of fiddling you can make uninitialized data sections overlap, with the region automatically accommodating the size of the largest. (I typically have a conditional error that ensures the region doesn't end up out of bounds - for example, with the above setup, there'd probably be a check to ensure the area doesn't go past $b0, or perhaps $c0, depending on the situation.)

The 64tass manual is worth raiding for good ideas: https://tass64.sourceforge.net/

--Tom
User avatar
Rich Talbot-Watkins
Posts: 2054
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: BeebASM assigning variables in macros

Post by Rich Talbot-Watkins »

Instead of adding new keywords for this, I'd just try to make ASM work with assigning variables too. Seems like the simplest and most obvious solution to me.

The original problem of assigning variables inside a macro which needs to be hoisted out of the macro instance's scope still wouldn't be solved by this of course.

I had a WIP sequel under development which I never have time to work on. That allowed you to do things like:

Code: Select all

.label
{
    .inner RTS
}

JSR label.inner
You could equally use that to create scoped variables like sysvia.t1cl or whatever. I considered a bit of syntactic sugar which would essentially be turned into:

Code: Select all

SECTION sysvia
ORG &FE40
.sysvia
{
    .irb .orb SKIP 1
    .ira .ora SKIP 1
    .ddrb SKIP 1
    .ddra SKIP 1
    \\ etc
}
ENDSECTION
I also had sections with their own instruction pointer, as alluded to above. They were actually the new mechanism for being able to assemble parallel sections of code, without needing CLEAR.
tom_seddon
Posts: 889
Joined: Tue Aug 30, 2005 12:42 am
Contact:

Re: BeebASM assigning variables in macros

Post by tom_seddon »

64tass has something of a solution for names in macros: if you have a label for the line the macro was invoked on, that counts as the namespace for any labels declared inside the macro.

Here's a quick example, generating a table of 255-terminated strings and separate LSB/MSB tables for their addresses.

Code: Select all

strings_list .macro strings

addresses:=[]
strings:
                .for i=0,i<len(\strings),i+=1
addresses:=addresses..[*]
                .text \strings[i],255
                .next

l:
                .byte <addresses
h:
                .byte >addresses
                .endm
And here's an example use. Note that l and h declared inside the macro now have the names command_lines.h and command_lines.l, because command_lines was the label for the line the macro was invoked on.

Code: Select all


		<<snip>>
                tax
                ldy command_lines.h,x
                lda command_lines.l,x
                tax
                jsr oscli
		<<snip>>

command_lines: .strings_list ["","LOAD !BOOT\r","RUN !BOOT\r","EXEC !BOOT\r"]
--Tom
jamesp
Posts: 8
Joined: Tue Dec 07, 2021 3:08 am
Contact:

Re: BeebASM assigning variables in macros

Post by jamesp »

ctr wrote: Thu Nov 02, 2023 8:26 pm DEFINE looks useful, though it's not obvious from the readme that it sets global variables. It could be renamed to GLOBAL to emphasise the point?
Yes, GLOBAL would probably be a better name. If this goes anywhere I can rename it easily enough.
I'm probably teaching you to suck eggs, but a common way to define variables is to use labels, for example:

Code: Select all

ORG &70
.ptr
    EQUW 0
.bvar1
    EQUB 0
.bvar2
    EQUB 0
My original use case for DEFINE was for creating struct like thing using MACRO which can be applied to multiple base addresses, specifically registers for multiple VIAs in my case.
So I'm not keen on ASSIGN. This feature has been requested before and allocating memory is nearly always the motivation. Another option is to add an explicit allocation feature. Perhaps some way of defining named regions and allocating from them. This would integrate with the existing code that checks that code blocks don't inadvertently overlap.
I'm also not that keen on ASSIGN, it solves the problem I had but does feel a bit dirty.

Built in support for defining regions of address space would satisfy my main use case for setting variables. I'm not keen on the reuse of ORG for switching to a different region though as it behaves differently to ORG taking a symbol or variable. Perhaps something like "INSIDE <region>" to switch to a region?

The other thing is you may well want to switch back to being able to pass any address to ORG, as I assume within a region ORG would be bounded by the extent of the region? If so, then a syntax for exiting the region or switching no no region would be needed.
Post Reply

Return to “development tools”