The best way to jump (JMP or JSR) to a pointer?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
klonoa
Posts: 59
Joined: Mon Nov 21, 2011 3:53 pm

The best way to jump (JMP or JSR) to a pointer?

Post by klonoa » Mon May 06, 2019 9:32 am

Hello my fellow nesdevers,

What is the best way to jump to an address held by a pointer?

The way I have it set up right now is that each of my gameobjects (16bytes each) that are held in a list in ram have their own update routine I call once during the NMI.
I could have just one ID byte for each gameobject and go through it like one big switch statement.

like this:

Code: Select all

;; GameObject in ram:
.db #ID, ,,,,other values

;;;; When i want to update a gameobject:
;X holds ID
CPX #PLAYER
BNE .NotPlayer
JSR PlayerUpdateRoutine
.NotPlayer
CPX #ENEMY
BNE .NotEnemy
JSR EnemyUpdateRoutine
.NotEnemy
;; And so on.
I want to avoid this cause if the Gameobject happens to be on the bottom of the list it would waste allot of time.
Instead I was thinking about this:

Code: Select all

;; GameObject in ram:
.db #ID,BehaviourAdrresLow,BehaviourAdrresHigh, ,,,,other values

;; Then in the update code I want to  jump to the address that is held in that piece of ram
;; This would also allow to easily swap behaviors if so desired.
What is the best way to do this?
At the moment I'm doing it like this:

Code: Select all

;; in ram:
GeneralOpcode .rs 1
GeneralPtr .rs 2
GeneralOpcode2 .rs 1
;;;;;;

LDA #$20
STA GeneralOpcode ;; store the JSR opcode
LDA #$60
STA GeneralOpcode2 ;;Store the RTS opcode

	LDA $0500,y ;;holds the BehaviourAdrresLow
	STA GeneralPtr
	INY
	LDA $0500,y ;;BehaviourAdrresHigh
	STA GeneralPtr + 1 

       ;;Store the returnadress on the stack
	LDA #High(ReturnLabel - 1)
	PHA
	LDA #low(ReturnLabel - 1)
	PHA
        JMP GeneralOpcode

ReturnLabel
;; rest of program
It works and I'm very happy it does but surely there must be a better way?

Thanks in advance!

tepples
Posts: 21949
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: The best way to jump (JMP or JSR) to a pointer?

Post by tepples » Mon May 06, 2019 9:56 am

For how to call a function through a function pointer, see Jump table and RTS Trick in the wiki.

User avatar
tokumaru
Posts: 11648
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: The best way to jump (JMP or JSR) to a pointer?

Post by tokumaru » Mon May 06, 2019 11:09 am

Your current solution looks a little too convoluted, with code in RAM and all... The idea is correct, but you can achieve the exact same effect in a much simpler way:

Code: Select all

  lda $0500, y
  sta GeneralPtr+0
  lda $0500+1, y ;no need to INY since the base address is absolute
  sta GeneralPtr+1
  jsr JumpToGeneralPtr ;this allows you to RTS here

  (...)

  ;anywhere else in ROM:

JumpToGeneralPtr:
  jmp (GeneralPtr)
A JSR to an indirect JMP simulates an indirect JSR, which the 6502 doesn't have. This is good if the update addresses are static (never change during the course of an object's life), but if it does, the RTS trick would be a better solution, because you can get a new RTSable address from the game logic itself just by JSRing to a location that'll get the address from the stack and save it in the object's memory. Something like this:

Code: Select all

 jsr CallObjectUpdate ;create a return point

  (...)

  ;somewhere else in ROM:

CallObjectUpdate:
  lda $0500, y
  pha
  lda $0500+1, y
  pha
  rts
And to modify the update address to the current location, and object can simply do:

Code: Select all

  jsr ChangeObjectUpdate

  (...)

  ;somewhere else in ROM:

ChangeObjectUpdate:
  pla
  sta $0500+1, y
  pla
  sta $0500, y
  rts ;return to where the object was called
If you don't need to change the update address, just RTS directly instead of calling ChangeObjectUpdate.

I like this dynamic approach to the object routines a lot, mostly because it makes state transitions easy, because you don't need a lot of code to sort out what an object is doing, the code that handles the current state is called directly. This also saves a bit of ROM and logic because you don't need separate "Initialize" and "Update" jump lists for the objects, you just need each object's initialization address, and the object itself takes care of maintaining its update address.

klonoa
Posts: 59
Joined: Mon Nov 21, 2011 3:53 pm

Re: The best way to jump (JMP or JSR) to a pointer?

Post by klonoa » Mon May 06, 2019 11:51 am

That's so clever.
I knew there was a better way.

I knew about indirect jumping and tried it but didn't seem to work.
Apparently nesasm uses [] instead of () :oops:

Anyway thanks for the quick reply! :)

supercat
Posts: 161
Joined: Thu Apr 18, 2019 9:13 am

Re: The best way to jump (JMP or JSR) to a pointer?

Post by supercat » Mon May 06, 2019 12:21 pm

tepples wrote:For how to call a function through a function pointer, see Jump table and RTS Trick in the wiki.
If you can afford dedicating 3 bytes of RAM to a springboard, and have fewer than 86 jump targets, have a page-aligned table of JMP instructions to each target, and a region of RAM that always holds $4C / target / JumpTableMSB. Then if X holds an object type, the code would be:

Code: Select all

    lda objectProcs,x
    sta springboard+1
    jsr springboard
where objectProcs is a table holding the low-order bytes of the jump targets within the table. The extra three cycles for a jmp at the destination will be cheaper than the cost of having to set up both bytes of the destination pointer, and in many cases one could eliminate that cost if one includes a few small routines directly within the jump table, e.g.

Code: Select all

NMIROUTINES:
nothing:
    inc frameCount
    rti
typicalThing1:
    jmp doTypicalThing1
typicalThing2:
    jmp doTypicalThing2
timeCriticalThing:
    ... code for timeCriticalThing goes here
Have the NMI vector point to a patchable JMP instruction in RAM, which will always point to something within a particular 256-byte page in ROM. Using this approach can shave a few precious cycles off the critical path, since one can eliminate the need to do any conditional tests before doing the time critical thing.

klonoa
Posts: 59
Joined: Mon Nov 21, 2011 3:53 pm

Re: The best way to jump (JMP or JSR) to a pointer?

Post by klonoa » Mon May 06, 2019 1:28 pm

supercat wrote:
tepples wrote:For how to call a function through a function pointer, see Jump table and RTS Trick in the wiki.
If you can afford dedicating 3 bytes of RAM to a springboard, and have fewer than 86 jump targets, have a page-aligned table of JMP instructions to each target, and a region of RAM that always holds $4C / target / JumpTableMSB. Then if X holds an object type, the code would be:

Code: Select all

    lda objectProcs,x
    sta springboard+1
    jsr springboard
where objectProcs is a table holding the low-order bytes of the jump targets within the table. The extra three cycles for a jmp at the destination will be cheaper than the cost of having to set up both bytes of the destination pointer, and in many cases one could eliminate that cost if one includes a few small routines directly within the jump table, e.g.

Code: Select all

NMIROUTINES:
nothing:
    inc frameCount
    rti
typicalThing1:
    jmp doTypicalThing1
typicalThing2:
    jmp doTypicalThing2
timeCriticalThing:
    ... code for timeCriticalThing goes here
Have the NMI vector point to a patchable JMP instruction in RAM, which will always point to something within a particular 256-byte page in ROM. Using this approach can shave a few precious cycles off the critical path, since one can eliminate the need to do any conditional tests before doing the time critical thing.

Oh wow that even more clever.
Not gonna lie, every time I ask for help here I feel a little dumb afterwards. :lol:

Post Reply