6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 9:05 am

All times are UTC




Post new topic Reply to topic  [ 12 posts ] 
Author Message
 Post subject: Marshalling X
PostPosted: Sun Jul 16, 2017 11:14 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
So, here's a brain-tease:

I'm using X for a data stack pointer. All's well. If on the off chance I want to use X for something else, I can temporarily PHX/PLX whilst I do so. A little awkward if I want to call a subroutine that expects X to be the stack pointer whilst I'm using it for something else, but not insurmountable.

HOWEVER!

I have found a situation where I can't think of a solution....
Say I wanted a jump table, which I access using
Code:
    LDX     #02
    JSR     ?local
?local:
    JMP     (TableBase, X)
   
TableBase:
    dw      Function1
    dw      Fucntion2
    dw      Function3


Obviously I have to save X and put it back when Function2 returns, first otherwise I'll corrupt my stack pointer. However, what if Function2 expects to be able to use X as the data stack pointer? It won't contain it, it'll contain the function offset instead, and there's no opportunity for me to put X back before Function2 starts executing. Function2 could assume that it needs to fetch the stack pointer value off the hardware stack, but that involves a lot of moving things around to achieve and means I can't generalise Function2 to situations where it isn't called from a jump table.

Ideal Solution:
Something I havn't thought of yet :roll: . Somehow allow jump tables.

Other Solutions:
  • Use Y instead of X as the pointer, not great as Y's unique addressing mode (zp),y is more useful than X's (zp,x) in the context of my API, so it would involve Y getting PHY/PLY more than with X. Also would break one of my existing data stack operations which uses the (Abs, X) addressing mode.
  • Put the data stack pointer in zero page instead of X. This is what I did originally, but I'd rather avoid it as it leads to slower & larger code.

---

Thoughts?

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Last edited by Alarm Siren on Mon Jul 17, 2017 9:13 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 1:13 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Alarm Siren wrote:
Put the pointer in zero page instead. This is what I did originally, but I'd rather avoid it as it leads to slower & larger code.
Another drawback is this approach isn't reentrant (unless you're willing to push the z-pg ptr to stack later). Better to just use the stack in the first place.

At the moment nothing occurs to me which entirely solves the "bigger & slower" problem. But here's a variation on your original approach which isn't too bad:

Code:
    LDY     #02         ;use Y, not X
    JSR     ?local
?local:
    LDA TableBase+1, Y  ;fetch the high-byte
    PHA
    LDA TableBase, Y    ;fetch the low-byte
    PHA
    RTS                 ;with 65xx family, RTS is a GOTO to top-of-stack PLUS ONE
   
TableBase:
    dw      Function1-1
    dw      Fucntion2-1
    dw      Function3-1
.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 1:18 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1950
Location: Sacramento, CA, USA
Hugh Aguilar has stated that he wished for a JMP (table,Y) instead, for probably the same reasons as you. But it's not to be, so you will have to construct a less-efficient work-around. One possible example:
Code:
    ...
    ldy  #4             ; point to third entry in jump table
    jsr  ?local
    ...
?local:
    lda  table+1,y
    pha
    lda  table,y
    pha
    php
    rti
    ...
table:
    dcw  target0        ; table offset 0
    dcw  target1        ; table offset 2
    dcw  target3        ; table offset 4
    ...


Mike B.

[ ... ooh, Jeff trumped me by a couple of minutes!]


Last edited by barrym95838 on Mon Jul 17, 2017 3:21 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 2:36 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
65xx Forth uses a ZP (or DP) variable called XSAVE (or similar) for that (although there aren't many places in the kernel that need to do this. That's the lowest overhead solution I can think of, outside of the '816 which has TXY and TYX which are one byte and two cycles each. You could have something like:
Code:
    STX     XSAVE
    LDX     #02
    JSR     ?local
?local:
    JMP     (TableBase, X)
   
TableBase:
    dw      Function1
    dw      Fucntion2
    dw      Function3

Function1:
    LDX     XSAVE
    <continue>

Function2:
    LDX     XSAVE
    <continue>

Function2:
    LDX     XSAVE
    <continue>

I assume the reason you want this is because you want to be able to modify the jump table without having to modify the places that call it. Otherwise you might as well just put the subroutine's actual direct address in the JSR instruction.

XSAVE in Forth never needs saving for re-entrancy because:
  • it's only used inside of primitives; and
  • Forth is always finished with XSAVE when the primitive finishes; and
  • you can let a primitive finish executing before servicing interrupts in Forth.

But if you did need it re-entrant, you would push XSAVE's contents to the stack. The '816 can push it directly with PEI (DP) without affecting A or P, in 2 bytes, 6 cy. The '816 also has JSR (addr,X), with which you could replace the JSR ?local and the label and the JMP (TableBase, X).

_________________
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  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 9:15 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
Dr Jefyll wrote:
Alarm Siren wrote:
Put the pointer in zero page instead. This is what I did originally, but I'd rather avoid it as it leads to slower & larger code.
Another drawback is this approach isn't reentrant (unless you're willing to push the z-pg ptr to stack later). Better to just use the stack in the first place.


I think you may have misunderstood my intent in the quoted section, which I have now updated to be more clear, but I meant using zero page for the data stack pointer itself instead of X. I suppose I could construct the jump table address in ZP and use JMP (abs), which is what I think you thought I meant, but as you say that wouldn't be reëntrant.

GARTHWILSON wrote:
I assume the reason you want this is because you want to be able to modify the jump table without having to modify the places that call it. Otherwise you might as well just put the subroutine's actual direct address in the JSR instruction.


There are two particular situations in which I envisage using jump tables as part of my BIOS / API.

One is user code makes API calls via a table of subroutine addresses. The point is that it means my internal implementations of the BIOS functions can change / move without causing a breaking change. For this purpose the following code, which doesn't use X, will do just fine:
Code:
    JSR     ?local
?local:
    JMP     (TableBase+(1*2))
   
TableBase:
    dw      Function1
    dw      Fucntion2
    dw      Function3


The other situation is something like in the UART IRQ handler, where I do something along the lines of:
Code:
   LDA UART_ISR
   AND #$0F   ; Mask out the four high bits which we're not interested in.
   ASL A      ; Shift one left because addresses are 2 bytes, not one.
   TAX       
   JMP (UartIrqJumpTable, X)
   KDATA
     UartIrqJumpTable:
     dw  UartMsrChangeHandler          ; ISR[3:0] = 0000 = MSR has changed
     dw  IrqHandlerEnd               ; ISR[3:0] = 0001 = No UART Interrupt
     dw  UartTransmitterEmptyHandler   ; ISR[3:0] = 0010 = Transmitter FIFO is Empty
     dw  IrqHandlerEnd               ; ISR[3:0] = 0011 = No UART Interrupt
     dw  UartDataFullHandler          ; ISR[3:0] = 0100 = Receiver FIFO is Full.
     dw  IrqHandlerEnd               ; ISR[3:0] = 0101 = No UART Interrupt
     dw  UartDataReceivedHandler      ; ISR[3:0] = 0110 = Some data has been received
     dw  IrqHandlerEnd               ; ISR[3:0] = 0111 = No UART Interrupt
     dw  IrqHandlerEnd               ; ISR[3:0] = 1000 = Invalid
     dw  IrqHandlerEnd               ; ISR[3:0] = 1001 = No UART Interrupt
     dw  IrqHandlerEnd               ; ISR[3:0] = 1010 = Invalid
     dw  IrqHandlerEnd               ; ISR[3:0] = 1011 = No UART Interrupt
     dw  UartDataTimeoutHandler      ; ISR[3:0] = 1100 = Data in Receiver FIFO and timeout period elapsed
     dw  IrqHandlerEnd               ; ISR[3:0] = 1101 = No UART Interrupt
     dw  IrqHandlerEnd               ; ISR[3:0] = 1110 = Invalid
     dw  IrqHandlerEnd               ; ISR[3:0] = 1111 = No UART Interrupt
   ENDS


Obviously within IRQ Hanlder I can just PHX/PLX as I deliberately don't use (or need) the data stack in IRQs, but I would like to be able to use a similar construction in user code.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 10:25 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
My feeling is that it's quite ambitious to use any of the A, X, Y registers as global state - there are so few registers that subroutines are likely to need them. So as a general design principle, I'd expect to see global state mostly in zero page, and loaded into registers for use when appropriate.

That said, it also seems pretty common to have one or two zp locations designated to store registers temporarily. Acorn's MOS has a location to store A for the ISR, for example, so it can use A to inspect the pushed value of P. Therefore, outside an ISR, and in a routine which would never be called from an ISR, that location is free scratch space.
http://mdfs.net/Docs/Comp/BBC/OS1-20/DC1C


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 12:13 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
In the OS I'm writing I decided to just have a table of function addresses at the start of the ROM (in a fixed location) and its the callers problem to indirect through them, typically with a JSR to a JMP () as below.
Code:
.loop:
        lda #'A'
        jsr DoThis
        jsr DoThat
        bra .loop

DoThis:
        jmp ($e000)
DoThat:
        jmp ($e002)

This is quicker than mucking about with the stack and leaves all the registers free for parameters. As both my assembler and WDC's support libraries I can write a set of stub functions once and use them over and over.

_________________
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  
 Post subject: Re: Marshalling X
PostPosted: Mon Jul 17, 2017 11:27 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
BigEd wrote:
My feeling is that it's quite ambitious to use any of the A, X, Y registers as global state - there are so few registers that subroutines are likely to need them.


I am increasingly inclined to agree, but I still want to avoid storing a stack pointer in ZP, so I've decided to scrap my current seperate data stack subroutines and replace them with macros that implement equivalent (and in some cases, more) functionality using the normal stack. Said macros always overwrite X (because TSX) but this is more workable as X is only being occassionally overwritten, it isn't permanently reserved.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Sat Sep 26, 2020 9:25 pm 
Offline

Joined: Sat Sep 26, 2020 6:46 pm
Posts: 1
Hello All ! Long time lurker :mrgreen:

I used to have an id here I think but it's been so long..... I've read posts here for years mostly invisibly. You folks are awesome and I appreciate the website and forums esp. very much. Here's something I found from a few years ago while reading reading reading....
I don't know if it can still help the original poster Alarm Siren, it's such an old thread, but I think I see a solution that I did not see from anyone else. And it might add to the ideas for others.

Alarm Siren wrote:
So, here's a brain-tease:

I'm using X for a data stack pointer. All's well. If on the off chance I want to use X for something else, I can temporarily PHX/PLX whilst I do so. A little awkward if I want to call a subroutine that expects X to be the stack pointer whilst I'm using it for something else, but not insurmountable.

HOWEVER!

I have found a situation where I can't think of a solution....
Say I wanted a jump table, which I access using
Code:
    LDX     #02
    JSR     ?local
?local:
    JMP     (TableBase, X)
   
TableBase:
    dw      Function1
    dw      Fucntion2
    dw      Function3


Obviously I have to save X and put it back when Function2 returns, first otherwise I'll corrupt my stack pointer. However, what if Function2 expects to be able to use X as the data stack pointer? It won't contain it, it'll contain the function offset instead, and there's no opportunity for me to put X back before Function2 starts executing. Function2 could assume that it needs to fetch the stack pointer value off the hardware stack, but that involves a lot of moving things around to achieve and means I can't generalise Function2 to situations where it isn't called from a jump table.

Underlined quote. That's the trick really. To be able to use a vector table method AND inline method.
I thought of this :

Code:
    PHX        ; or STX zp_somewhere  for NMOS
    SEC              ;  flag indicating table method call !
    LDX #2
    JSR TABLECALL
    BVC ?TBLSKIPJMP   ; we keep V flag clear for pseudo BRA
?TABLECALL
    JMP (TABLEBASE,X)
?TBLSKIPJMP
     ...<more code>

FUNCTION2:
     BCC  ?SKPTBL
     PLX        ; retrieve X stack ptr
?SKPTBL
    ...<function2 code matching inline method calls>
     RTS

This way the Carry is used as a method flag whether inline or table, and very little extra code needed.

THEN I thought of this way -- If you named each function1,2,3 etc with their own assembly labels instead of one
top label, this is possible...
Code:
   PHX
   LDX #2
   JSR -> JMP (TABLEBASE,X)   ; for table method calls.

and only the PLX starting each function1,2,3,etc without BCC...

Then for inline function1,2,3 calls with each functions own label/symbol you can
Code:
   LDX #2
   JSR   TABLEFUNCTION2 + 1     ; skips the PLX within the function.  BCC never needed.



The latter format is best. One more byte per table method call, and a assemble-time adjusted inline method call that costs nothing.

=peaceful world


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Sat Sep 26, 2020 9:43 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Frankly, I would just reserve some memory location to hold the data stack pointer, and treat the X register as merely a cache of it. If you have some other short-term use for the X register, you just save and restore around it. Even if the memory is not in ZP, that's still merely an 8-cycle penalty, which is not very much on the 6502.


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Wed Oct 07, 2020 7:56 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
Thank you for your input _falcon_, I too have now become a long-time lurker! I tend not to talk about what I'm doing anymore until I've got something concrete, because very few of my grand plans ever get beyond the planning stage. Similarly, and to be honest, the project this was for has long fallen by the wayside, but its an interesting approach nevertheless.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
 Post subject: Re: Marshalling X
PostPosted: Thu Oct 08, 2020 2:22 am 
Offline

Joined: Wed Jan 08, 2014 3:31 pm
Posts: 578
BigEd wrote:
My feeling is that it's quite ambitious to use any of the A, X, Y registers as global state - there are so few registers that subroutines are likely to need them.

I view the accumulator as volatile, but follow the policy of saving X or Y before use, and restore when done. So I'll phy at the start of a routing, and then ply on exit. That way my subroutine can be called in another that's using Y, or call itself. I often use X as a data stack pointer in zero page, but the same policy works for it as well.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC


Who is online

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