7

I may be going about this the wrong way, so sorry if it is wrong, I'm coming to 6502 after 32 years (when I was 8 years old !)

I want to pass a pointer to some data, either data1 or data2 to a sub routine, which I can then access in the subroutine via lda data1, y or lda data2, y. Something like this, although of course it's not right:

            // send in data1 or data2 to subroutine... e.g. push onto the stack maybe?
            jsr function
            rts

function:
            ...

            lda ***, y         // how can I replace *** with data1 or data2 here, sent in from the main program?
            sta memlocation, y

            rts

data1:      .byte 45, 50, 75, 50, 105, 50, 135, 50, 165, 50, 195, 50, 225, 50, 255, 50
data2:      .byte 45, 100, 75, 100, 105, 100, 135, 100, 165, 100, 195, 100, 225, 100, 255, 100

Could indirect addressing work somehow? I'm a bit confused about this, but maybe I'm trying to achieve this the wrong way.


Background:

Although I do own a C64, I'm currently developing on Kubuntu Linux using Visual Studio Code with Kick Assembler, the VCS Kick Ass extensions, and VICE64 (all free !)

I just got 72 sprites on screen at once :-) but I'm trying to optimise my routines.


Thanks to the answer I used a global parameter, and have reduced my code footprint significantly:

enter image description here

  • 1
    For questions like this it may be useful to add which assembler you use, as some may require a specific syntax. Also, while not so important in this case, adding what machine it is for is always a nice idea. – Raffzahn Feb 15 at 23:55
  • 2
    8 years old and using asm! ouch. I was 13 and it already was tough. – Jean-François Fabre Feb 16 at 1:53
  • 1
    @Jean-FrançoisFabre yes, my dad made a helicopter game and taught me a thing or two. I didn't do much with it but it's stuck with me all these years :-) – Pixel Feb 16 at 7:45
  • Your dad wasn't Dan Gorlin was he? – Glen Yates Feb 17 at 17:34
  • @GlenYates no, nothing that fantastic ! He took a year out with his brother to try to make a game, but the money ran out and the project fizzled away. I've no idea what happened to the game, I'll have to ask next time I see him. I think they sold one copy then had to give a refund :-O – Pixel Feb 17 at 18:39
6

How to pass data pointer to subroutine to use with Absolute, Y addressing?

That's simply not possible - unless you want to do self modifying code - which is rather uncool. If you really want, it would look like this:

       ...
       LDA   #<data1
       LDY   #>data1
       JSR   Function
       ...
       LDA   #<data2
       LDY   #>data2
       JSR   Function
       ...

Function:
       STA   LocPtr
       STY   LocPtr+1

       LDY   #0
       LDA   $1000,Y    ; Dummy value to force two byte address (*1)
LocPtr EQU   $-2
       ...

Could indirect addressing work somehow?

Yes, for more serious programming Indirect Indexed is the way to go. Either use a global parameter field(s) (which I'd suggest) or (semi-) local.

a) Use of global parameter:

Param  DW    0      ; Somewhere in ZP

       LDA   #<data1
       STA   Param
       LDA   #>data1
       STA   Param+1
       JSR   Function
       ...
       LDA   #<data2
       STA   Param
       LDA   #>data2
       STA   Param+1
       JSR   Function
       ...

Function:
       LDY   #0
       LDA   (Param),Y
       ...

b) Use of semi local fields:

Param  DW    0      ; Somewhere in ZP (but more of a scratchpad)

       ...
       LDA   #<data1
       LDY   #>data1
       JSR   Function
       ...
       LDA   #<data2
       LDY   #>data2
       JSR   Function
       ...

Function:
       STA   Param
       STY   Param+1

       LDY   #0
       LDA   (Param),Y
       ...

It's called 'semi' as the pointer still has to reside at an absolut (ZP) location. Jsut parameter passing is done in registers. Of course this could even be made 'real' local by saving the ZP location onto the stack and restoring it from there at the end. That's useful if one has no idea about memory usage. But quite slow - and n addition dangerous if the location choosen is used by some (interrupt) coroutine as well.

Personally I prefer to setup global parameter / work areas in ZP as they offer many benefits in speed and size.


*1 - I know it's not needed as there is no ZP,Y addressing, but it's good practice to always assume as if and supply a value forcing 16 bit addressing. If by any editing error some other addressing is chosen, the assembler will then fulfil its servitude and issue a helpful warning.

  • 1
    self modifying code was usual at the time even on 68000 (unless code ran from ROM) because it kind of saved cycles. But I agree it's bad practice. – Jean-François Fabre Feb 16 at 2:48
  • 1
    self-modifying code was cool until when CODE segments became RX by default, when we could no longer use the original optimized Bresenham algorithm in gfx dev. When and why that happened, and if what has been a widespread optimization technique must now be considered bad practice even on a 6502 is material for several further questions. – dlatikay Feb 16 at 14:40
  • @dlatikay Self modifying code has always been present (I know it from mainframe code as far back as the 1960s) and it was never cool with anyone. It was always just a hack and resulting in higher maintenance cost as well as severe long term risks. These may not have been as visible to some special areas and one time code. The effect was still the same. Also, there's a huge difference between self modifying code and run time code generation. – Raffzahn Feb 16 at 17:19
  • @Raffzahn: I'm not sure what you regard as the "huge difference" between self-modifying code and runtime code generation. There are many situations where the only things that gets modified are instruction operands but the structure of code is static. If one needs to do a memory-copy operation on the 6502, incrementing the upper bytes of the load and store instructions within the loop doesn't seem any worse than building a function to copy 256 bytes, calling it, modifying it, calling it again, etc. What would the extra calls accomplish? On some processors, it would be necessary... – supercat Feb 16 at 18:16
  • 1
    @supercat the huge fundamental difference is the "self" part. It adds an extra level of complexity. – JeremyP Feb 22 at 14:21
7

In addition to Raffzahn's answer: If you're in a rush and are executing from RAM, consider self modifying code. This is useful if the LDA base changes frequently enough so that it isn't constant, but does keep its value within a tight loop.

Place a label before the use of the absolute indexed by Y form of LDA in your code. Then you modify the constant base address part of the LDA instruction directly. The machine code format of LDA $hhll,Y is $39 ll hh. You can modify the values of ll at label+1 and hh at label+2. The modification can be done either:

  • within the routine - you'll have to pass into your routine (through global RAM as suggested by Raffzahn, or in registers, or on the stack) the 16-bit table base address itself, or a simpler 8-bit index to a table of table base addresses which you then have to dereference, or
  • outside of the routine - which would make that modifying that instruction part of the calling convention of that routine: the RAM storing the address part of the LDA is now 'a parameter' you set before you call it.
  • "would make that modifying that instruction part of the calling convention" Ouch. While it really is (in most cases) the fastest way to go, it's also the most error prone. Usually a great source of hard to find errors when working with interrupts as well. A rough training ground for reentering 6502 programming, isn't it? :) – Raffzahn Feb 16 at 0:12
  • 1
    I'm tempted to use this approach, as I know I will have to save as many cycles as I can later on, and this is what the "cool kids" were doing back in the day too. On the other hand I know it's naughty ! :-) – Pixel Feb 16 at 7:58
  • @Pixel Only as 'cool' and 'naughty' as racing toward a cliff or an other shortsighted measure as a dare. Dangerous programming as tool of premature optimization. Self modifying code should usually be the last resort not the first, shouldn't it? – Raffzahn Feb 16 at 8:33
  • 1
    I voted your answer up because I do this kind of thing all the time. It's really not dangerous! It's exactly as safe and maintainable as keeping the pointer anywhere else. Provided you use the assembler properly. – Wilson Feb 16 at 21:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.