Grevle Posted March 6, 2020 Share Posted March 6, 2020 In Atari basic and turbobasic its practical to gosub variables , Like This example: 10 X=100 50 Gosub X 60 stop 100 print "Atari Rules":return So the program vil gosub the line corresponding to the value stored in X, Practical to redirect program routines. In assembler this should be possible ? but im doing something wrong. Example using equates: X=35000 ; x=adress 35000 JSR X ; Well it jumps to adress 35000 but not to whats stored in X. maybe one have to use some low byte and high byte calculations and the tax command perhaps ? Im not exactly sure how to go about this, So any help would be useful.. Quote Link to comment Share on other sites More sharing options...
+Eyvind Bernhardsen Posted March 6, 2020 Share Posted March 6, 2020 You have to use the indirect mode. I think it’s “JSR (X)”, but it’s been a long time since I wrote any 6502 assembly. Quote Link to comment Share on other sites More sharing options...
Grevle Posted March 6, 2020 Author Share Posted March 6, 2020 me too and i'm still a beginner , well maybe its thats simple.hope so. Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted March 6, 2020 Share Posted March 6, 2020 (edited) 9 hours ago, Eyvind Bernhardsen said: You have to use the indirect mode. I think it’s “JSR (X)”, but it’s been a long time since I wrote any 6502 assembly. Close, you'd ned to use a JSR to a JMP CallFunc: .byte $4C ; JMP abs FuncAddr: .word 0 ... .macro SetFuncAddr addr LDA #<(:addr) STA FuncAddr LDA #>(:addr) STA FuncAddr+1 .endm ... ThisAndThat: SetFuncAddr DoThis JSR CallFunc SetFuncAddr DoThat JSR CallFunc JMP ThisAndThat DoThis: LDA #0 STA COLOR3 RTS DoThat: LDA #15 STA COLOR3 RTS Edited March 7, 2020 by Wrathchild (wrong JMP) Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 7, 2020 Share Posted March 7, 2020 Hi! 2 hours ago, Grevle said: So the program vil gosub the line corresponding to the value stored in X, Practical to redirect program routines. In assembler this should be possible ? but im doing something wrong. First, from a theoretical point, if something is possible in BASIC, it is also possible in assembler, as Basic is written in assembler Now, the 6502 does not have a specific instruction to do that: call a subroutine to an address from a register or a memory location. The standard solution to that in computer sciences is called a "trampoline": You write a subroutine that jumps to the correct location and you call this subroutine (the trampoline) instead. Two possible trampolines are: 1.- Code that is "patched" at runtime with the address to jump to (this is how a trampoline is usually written in many computer architectures). Easiest is to reserve three instead of two location for the address: ; Call our fucntion via the trampoline lda #<procedure1 sta target lda #>procedure1 sta target+1 jsr trampoline ; ........ ; more code here.... rts trampoline: byte $4c ; $4C is the "JMP" instruction target: byte $00 byte $00 The advantage of this solution is that if the address to call (the value of "X" in your example) does not change a lot, this is fast. On most current operating-systems this king of trampolines is used a lot to call to operating-system code (or code from other modules), as you don't know the address of the target until you load it in memory, so you create one trampoline for each function that you want to call. And, if you store the entire trampoline (the 3 bytes) in zero page, the loading of the address is also faster and smaller. 2.- Using the 6502 "indirect jump" instruction. This instruction jumps to an address stored at a memory location, so it looks like: ; Call our fucntion via the trampoline lda #<procedure1 sta target lda #>procedure1 sta target+1 jsr trampoline ; ........ ; more code here.... rts trampoline: jmp (target) ; Indirect jump - go to the address stored in target and target+1 target: byte $00 byte $00 As you see, here the trampoline is 2 bytes bigger, and also 2 cycles slower (as the 6502 needs to read two more bytes). If you store the target in zero-page location, then the code that set a new address is 2 bytes smaller and 2 cycles faster, but only if you set the target before each call. So, this solution is only good if the target address is already set (for example, as part of the operating-system variables) as you don't need to load the address to the trampoline before using. Have Fun! Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted March 7, 2020 Share Posted March 7, 2020 There is also an advantage of the 2nd (indirect) option as this can be used direct from ROM (cartridge) code where the 'target' is then stored in the RAM area and so can be modified. With the 1st version the trampoline JMP cannot be located in ROM as any store to the target address has no affect. Therefore those 3 bytes would instead have to be relocated to RAM and assembled with a different run address so that calls to it resolved to the relocated address. Quote Link to comment Share on other sites More sharing options...
Xuel Posted March 15, 2020 Share Posted March 15, 2020 Another trampoline option is to push the target address minus one on the stack and then RTS. Upside is it only takes stack RAM. Can also be smaller code-wise. Downside is that your function pointers all need to be one less then the target address since RTS sets PC to the address on the stack, then adds one. Also a little slower. See also: https://wiki.nesdev.com/w/index.php/RTS_Trick Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.