"Pass by reference" with a data stack

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Post Reply
Paganini
Posts: 516
Joined: 18 Mar 2022

"Pass by reference" with a data stack

Post by Paganini »

So I have this little routine:

Code: Select all

_LCD_strout:
    ldy    #0
LCD_strout_nextchar:
    lda    (R0),y
    beq     LCD_strout_return
    jsr     _LCD_chrout     ; sends the character in .A to the LCD
    iny
    jmp     LCD_strout_nextchar
LCD_strout_return:
    rts
"R0" is a 16bit pseudo-register in ZP. Somewhere in ROM there's an asciiz message constant.

Code: Select all

R0      = $80
R0L     = $80
R0H     = $81

.org    $8000

message:    .asciiz     "Hello, world"
I call the routine like this:

Code: Select all

    lda     #<message
    sta     R0L
    lda    #>message
    sta     R0H
    jsr     _LCD_strout
This is works perfectly well. However, for unrelated reasons, my firmware code is starting to get unwieldy, and I thought now would be a good time to implement a ZP data stack like Garth describes and rewrite some of my routines. The ones like _LCD_chrout that just take a byte in .A I will leave alone. But _LCD_strout seemed like a good candidate for reworking, since it takes an address, and is called randomly from all over the place (in addition to using it for output, I also stick it in various places for debugging) which means nothing else can ever use R0, in case a string output call steps on it.

So I'd like to do something like this:

Code: Select all

STACK      = $0
STACKL     = $0
STACKH     = $1
reset:
    ldx     #$FF
    txs             ; Hardware stack -> $01FF
    dex             ; Data stack -> $00FE

; (later on)

    lda     #<message
    sta     STACKL,x
    lda     #>message
    sta     STACKH,x
    dex
    dex
    jsr     _LCD_strout
My question is, is there some way to leave the address on the stack while indexing into the string? It seems like I need the equivalent of (STACK,x),y in order to index into the string, and it is not obvious to me how to express that. If I have to pull the address off the stack and into a scratchpad variable it seems like I might as well just keep on using the "pseudo register."

Code: Select all

_LCD_strout:    ; Stack version
    ldy    #0
    inx
    inx
LCD_strout_nextchar:
    lda    (STACK,x),y      ; what really goes here?
    beq     LCD_strout_return
    jsr     _LCD_chrout     ; sends the character in .A to the LCD
    iny
    jmp     LCD_strout_nextchar
LCD_strout_return:
    rts
(I have read Garth's page about the magic of inlining parameters, but I'd like to do it this way first, if there is a way.)
"The key is not to let the hardware sense any fear." - Radical Brad
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: "Pass by reference" with a data stack

Post by Martin_H »

An "LDA (STACK,x),y" would be an awesome instruction for just the reason you site. When I am using a page zero data stack I tackle this in one of two ways:

I have a page zero location called TMPPTR and pop TOS into it, and then use "LDA (TMPPTR),y" but as you point out, you could just use a known page zero location to pass the argument. But using a data stack avoids slamming that address accidentally elsewhere. This avoids a reentrancy issue which can make for hard to find bugs.

Since string I/O isn't time critical, incrementing the pointer directly on the stack avoids needing to use the Y register or another page zero location. I increment the pointer on the stack directly using a macro called inctos which looks like this:

; increments the TOS value
.macro incTos
inc TOS_LSB,x
bne _over
inc TOS_MSB,x
_over:
.macend

Performance wise this isn't great because indirect Y addressing is much faster, but in some cases I don't think it is a problem.
Paganini
Posts: 516
Joined: 18 Mar 2022

Re: "Pass by reference" with a data stack

Post by Paganini »

Martin_H wrote:
Since string I/O isn't time critical, incrementing the pointer directly on the stack avoids needing to use the Y register or another page zero location.
AH! I'm equally split between "of course, why didn't I think of that, what a dope" and "that's super clever, you are a genius!" :D

I think that will be fine; this just needs to print out short strings on the LCD, which is already super slow. The added overhead of incrementing a 16-bit pointer will probably be unnoticeable. Thanks!
"The key is not to let the hardware sense any fear." - Radical Brad
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: "Pass by reference" with a data stack

Post by GARTHWILSON »

You can have both.  On the page you referenced, about 90% of the way to the bottom, see the paragraph starting with "I should mention here that some routines will make so many accesses", about "N" as a ZP scratchpad space for this kind of thing and is not to be used to pass parameters between routines.

You might find an efficient self-modifying-code (SMC) solution also, if the code doesn't have to be in ROM.  See the article at http://wilsonminesco.com/SelfModCode/ .
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?
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: "Pass by reference" with a data stack

Post by BigDumbDinosaur »

Martin_H wrote:
An "LDA (STACK,x),y" would be an awesome instruction...

Yep! You can do that with the 65C816, as in LDA (<offset>,S),Y, where <offset> is relative to the current value in the (16-bit) stack pointer (SP). You don't even have to know what’s in SP. :D
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
MichaelM
Posts: 761
Joined: 23 Apr 2012
Location: Huntsville, AL

Re: "Pass by reference" with a data stack

Post by MichaelM »

Second BDD’s response. Although my enhanced soft-core is not in a working state at the moment, I too included a full set of these type of stack-relative instructions in its instruction set. I also included a base-pointer relative mode, where I use X loaded with the value of S at the start of the subroutine, and use sign extension of the 8-bit offset to access local variables (negative offsets from the base pointer) and subroutine parameters (positive offsets from base pointer). Works well with the Pascal compiler that I ported to work with my extended 65C02 processor.

These accesses can be synthesized with the 6502 instruction set, but the stack-pointer relative addressing mode makes complex equation evaluations much easier. My base pointer relative addressing mode also can be synthesized, but it really make passing and using parameters in the stack much easier. Dereferencing pointers and data on the stack is so much faster with these addressing modes that including them in my processor model was well worth the additional hw complexity.
Michael A.
Post Reply