6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat May 11, 2024 11:09 am

All times are UTC




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sat Mar 18, 2023 1:05 pm 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
I have a decent handle on 6502 ASM. I'm trying to work out a new macro that can point to a sort of variable label and I'm running into a stumbling block. This may not be possible, but it seems with the component pieces, it should be.

Here is what I'm effectively trying to do, with bad syntax (I know this won't work, but for the concept):

Code:
.MACRO DoStuff arg0, arg1
    LDA arg0 + "_" + arg1 + ",y"
.endM


Where this...

Code:
DoStuff myLabel, first

;; or

DoStuff "myLabel", "first"


...would effectively give me:

Code:
LDA myLabel_first,y


Is there a way to do something like this?


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 2:30 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
What assembler are you using?

Syntax and macro capabilities vary greatly.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 2:40 pm 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
Gotcha.

ASM6.

I don't quite see any way to combine these literal strings to then be read as a single label this way, but it seems like it *should* be possible. There's just no way I can see to append strings like this (+ always looks for a number). But before I make the presumption, I just want to make sure there's no deeper hidden (and probably obvious) method I'm not considering.

I can easily make it a single argument and it functions as expected. But I can't figure out a way inline to combine the arguments to have it read as the single label to then work with.

Thanks!


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 3:25 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 388
Location: Minnesota
Some assemblers do have string concatenation as a native capability. One of those might help here.

Alternatively, you might try to substitute directly and see if that works. Instead of

Code:
.MACRO DoStuff arg0, arg1
    LDA arg0 + "_" + arg1 + ",y"
.endM


try:

Code:
.MACRO DoStuff arg0, arg1
    LDA arg0_arg1,y
.endM


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 3:39 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
with ca65 this could be easily done using the built-in ".sprintf" function that works exactly like sprintf in C:

Code:
.macro DoStuff arg0, arg1
   LDA .ident(.sprintf("%s_%s", .string(arg0), .string(arg1))),Y
.endmacro


".ident" just converts the resulting string from ".sprintf" into an identifier (ie a symbol or label).

then again, why seperate the label names only to them combine them again in a macro? like what exactly is the use case for this?
maybe with a better understanding of what you're trying to do, an alterantive could be worked out.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 4:49 pm 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Proxy wrote:
then again, why seperate the label names only to them combine them again in a macro? like what exactly is the use case for this?
maybe with a better understanding of what you're trying to do, an alterantive could be worked out.

That sort of thing can be useful when the calling site is also in a macro, and one of the components of the label name is an argument to that outer macro.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 5:05 pm 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
Direct subbing doesn't work (all manners and combinations of things I've exhausted).

As far as use case, I can think of a million, but let me try to come up with a functional one. Let's just say I have a program that draws various boxes. Each box is its own width, height, has its own contents. Effectively, it's a multi-dimensional array of data to form the box.

I could do this easy with tables...something like:

Code:
Box_lengths:
  .db #10, #20, #30
Box_widths:
  .db #15, #30, #45
Box_contents:
   .db #0, #1, #2


All good. I could draw this box with just a y offset to get the length, width, and contents for the Yth box and create a draw routine. Easy.

But now I have a box that might have variable data inside of it. So for contents, it might look more like this:

Code:
Box_0_contents:
   .db #0, #1, #2

Box_1_contents:
   .db #10, #11, #12, #13, #14


What I'd love to be able to do is something where I could, say, have a macro:

Code:
.MACRO SetUpBox arg0
    ; arg0 = box id.

Then I'd end up coding:

Code:
SetUpBox Box_0

...which could give me easy access to Box_0_contents as the label to point to to get that particular boxes contents, because in the macro itself, I could combine the argument (Box_0) with what table for that box I'm looking for (_contents), and read away.

The way that I would do it now is have some pointer table...something like:

Code:
BoxContents:
   .dw Box_0_contents
   .dw Box_1_contents
   ;; etc


...and then use the same offset for the box I'm looking for, have it point to the table, get the relevant data via indirect indexed addressing...whatever. I guess this system would avoid the need for this table and that sort of fetch. The routine could look up "Box_" + the identity of the box + "_" + the parameters and labels it needs to work with just by invoking a macro. But I can't quite figure how to do it with this assembler.

This is just an off the cuff example, but does it make sense as for as intent?


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 8:25 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
hmm, to me that sounds like you want to replicate the functionality of C structs (with dynamically sized elements).

so you could just copy exactly what structs in C do and use a pointer to dynamically sized elements instead of directly storing them in the struct itself.
which means they're slower to access, but simplifies the code required to deal with them as you don't need to worry about your structs being differently large.

for example a regular struct that deals with a box (position and size only) could be made by having the base address of the struct and adding a constant offset to it, depending on which element you want to access:

Code:
; Element Indices relative to the start of the Struct
x_pos   = 0     ; X Position    (16-bit)
y_pos   = 2     ; Y Position    (16-bit)
width   = 4     ; Width         (16-bit)
height  = 6     ; Height        (16-bit)
; Total of 8 Bytes


box0:
    .word 240   ; X Position
    .word 710   ; Y Position
    .word 25    ; Width
    .word 41    ; Height

box1:
    .word 942   ; X Position
    .word 291   ; Y Position
    .word 78    ; Width
    .word 19    ; Height


someFunction:
    LDA box0 + width        ; Low Byte of the Width element of Box 0
    LDX box0 + width + 1    ; High Byte of the Width element of Box 0
   
    LDY box1 + y_pos + 1    ; High Byte of the Y Position of Box 1
RTS


for dynamically sized elements, pointers (similar to how you're already doing it) seem like the best way to go. so here the same example again but with an added "contents" element that points to a NULL terminated list somewhere in memory:

Code:
.zeropage
zPtr:   .res 2

; Element Indices relative to the start of the Struct
x_pos       = 0     ; X Position            (16-bit)
y_pos       = 2     ; Y Position            (16-bit)
width       = 4     ; Width                 (16-bit)
height      = 6     ; Height                (16-bit)
contents    = 8     ; Pointer to Contents   (16-bit)
; Total of 10 Bytes

.data

box0:
    .word 240   ; X Position
    .word 710   ; Y Position
    .word 25    ; Width
    .word 41    ; Height
    .word box0_contents


box1:
    .word 942   ; X Position
    .word 291   ; Y Position
    .word 78    ; Width
    .word 19    ; Height
    .word box1_contents

box0_contents:
    .byte $4C, $90, $0A, $EC, $53, $7B
    .byte $00   ; Termination Byte (0 is easy to check for)

box1_contents:
    .byte $BA, $B7, $01, $DC, $4B, $20, $98, $4D, $4D, $E0, $37, $26, $1A, $40, $9E, $7A, $9C, $4D
    .byte $00   ; Termination Byte


.code

; Load the Contents of the box "baseBox" using Y as an index
.macro getBoxContent baseBox
    LDA baseBox + contents
    STA zPtr
    LDA baseBox + contents + 1  ; Get the Address to the contents of the selected box
    STA zPtr + 1                ; And put them into a Temporary ZP word
    LDA (zPtr),Y
.endmacro

; Example Function:
printContents0:
    LDY #0
    loop:
        getBoxContent box0
        BEQ exit
        JSR print_something
        INY
    BRA loop
    exit:
RTS

and if you want to be able to deal with multiple dynamically created structs at runtime, all your functions will have to do some math to calculate the addresses of elements relative to the starting address of the struct, and then use indirect addressing modes to access those. sadly there is no double-indirect addressing mode so when accessing a pointer you have to save it to ZP first before being able to use it.

i hope this was atleast somewhat helpful and is what you were looking for in terms of functionality.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 18, 2023 11:56 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8433
Location: Southern California
Quote:
sadly there is no double-indirect addressing mode

It it goes in RAM, you can use self-modifying code. :D

_________________
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 Mar 19, 2023 12:27 pm 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
Proxy - yeah, that's not so dissimilar to what I'm actually doing now. Lot's of pointer / indirect look ups. It get's more ugly when a system has multiple properties with multiple values...recursive indirect lookups to bog things down in what seems like a needless way since the root text of multiple tables is always the same. But there doesn't seem to be a way in this assembler to account for that in the way I was hoping.

Was worth a shot. :-)


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 20, 2023 2:50 am 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
Proxy, on that note...help me with this one as an alternative way to *end* the contents rather than null (in the event it's a situation where I need all 256 values). I can conceive of something like this when I KNOW the label in question:

Code:
;; here is a contents table
myContents0:
  .db #0, #1, #2, #3
  .db #4, #5, #6, #7
  .db #8, #9, #10
myContents0_length = $-myContents0
;; nifty way to get a count, so long as it's 256 vals or less - should be fine.


...and use that with something like this:

Code:
ldy #0
doLoop:
  lda myContents0,y
  ;;; do stuff
  iny
  cpy #myContents0_length   ;; this will be the problem line when variable, explained below
  bne doLoop
;; done



No problem. That works fine...presuming we're dealing with a static address (myContents0).

But I guess I get a bit confused when I start factoring in pointers. If I end up utilizing zpPointer know where doLoop should be fetching (works fine), how can I do this exact compare? Obviously #(zpPointer),y won't be correct. Is there a way to utilize this nifty way of tracking the length this way but with a variable pointer value rather than a static value? Maybe this is easy and I'm thinking too hard about it.

Thanks!


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 20, 2023 4:56 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
I'm not exactly sure what you mean, as long as your data is completely static and known at assemble time you can use "myContents0_length" to do this no matter if you access the data directly or indirectly through a pointer.

but if you meant that the amount of data can change at runtime (ie the assembler doesn't know how much data the contents table will have) then you just need to add another variable to your struct that keeps track of that, and manually update it when you add or remove values from the contents table.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 20, 2023 11:21 am 
Offline

Joined: Sat Mar 18, 2023 12:50 pm
Posts: 6
Alright - I'm not getting expected results there, so I'm guessing I'm asking a syntax question.

This is part of a Macro / subroutine combo so that I can reuse the functionality with different elements, in each case, different, but it is static at time of assembly. If I have proper index in y, and the address of myContents0 is loaded into (zpPointer), and that position should return myContents0_length = $-myContents0, comparing against (zpPointer),y does not work as expected. I guess what is needed is immediate indirect addressing...like #(zpPointer),y? But that does not work as expected either.

So manually, the example works perfect (when I actually put in the label as above). However, trying the same trick through indirect addressing does not seem to work. Just seeing if anyone can give me a clue as to what's wrong with the syntax, or a proper way to do this? Thanks!

EDIT: Was making this harder on myself than I had to. I got it working.

If anyone is looking for a solution for this, the solution is simple. The table ends up looking like this:

Code:
myContent0:
  .db #1, #2, #3
  .db myContent0_length = $-myContent0


...and then it's just reading that value via the pointer. So far, as long as myContent's number of values < 256, this seems to work fine, and I could conceive of how to do it with 16 bits as well if needed.

Thanks for the help, guys.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 20, 2023 2:25 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 388
Location: Minnesota
Quote:
Is there a way to utilize this nifty way of tracking the length this way but with a variable pointer value rather than a static value?


One way is to use decrementing rather than incrementing. Instead of:

Code:
  ldy #$00
doLoop:
  lda myContents0,y
  ;;; do stuff
  iny
  cpy #myContents0_length   ;; this will be the problem line when variable, explained below
  bne doLoop
;; done


try:

Code:
doLoop:
  lda myContents0,y
  ;;; do stuff
  dey
  bne doLoop
;; done


...so the entry point is 'doLoop' (or whatever better name you think of). The point is that you can load the Y-register with whatever value you want just before calling this routine. It doesn't have to be the same value each time.

or if you really want to increment:

Code:
  sty temp
  ldy #$00
doLoop:
  lda myContents0,y
  ;;; do stuff
  iny
  cpy temp
  bne doLoop
;; done


...again pre-loading Y with the correct terminating value just before calling this routine.

If you find that most calls will use the same terminating value:

Code:
static_entry:
  ldy #common_terminate
variable_entry:
  sty temp
  ldy #$00
doLoop:
  lda myContents0,y
  ;;; do stuff
  iny
  cpy temp
  bne doLoop
;; done


...and call 'static_entry' most of the time and 'variable_entry' when you have something less common to do.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 20, 2023 2:36 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
This discussion has meandered on and I am not clear exactly what you seek to accomplish.

As for your original question, sometimes a more primitive assembler can be more useful.

The FLEX 6809 assembler macro facility does not use names for parameter substitution. Instead, you use &1, &2, etc based on the position on the macro invocation line.

For example, in my cross assembler,

Code:
                          00001 M        macro
                          00002
                          00003          lda    &1_&2,Y
                          00004
                          00005          endm
                          00006
                          00007          M      a,b
+
+0000 B9 0000       [4/5]                lda    a_b,Y


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 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: