Why does this 6502 code push a function address onto the stack before calling?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
2
down vote

favorite












I am learning how to program the Atari 800 by examining some tutorial code that came with the IDE/Assembler I am using. I am using the MADS assembler for this.



putchar_ptr = $346
csrhinh = 752
character = $80
rowcrs = $54
colcrs = $55
org $2000

.proc main
mva #1 csrhinh
mva #6 rowcrs
mva #16 colcrs
mva #0 character

next_character
ldx character
cpx #.len text
beq stop
lda text,x
jsr putchar
inc character
jmp next_character

stop jmp stop

.proc putchar
tax
lda putchar_ptr+$1
pha
lda putchar_ptr
pha
txa
rts
.endp

.local text
.byte 'Hi there!',$9b,'new line'
.endl

.endp

run main


I did reference the Atari 800 manual and the MADS-Assembler manual but I didn't find anything. The specific question I am asking is, in the putchar procedure, why is the accumulator pushed onto the stack? From what I can tell all it is loaded with is the location of the routine pointer on the first push and the put pointer on the second. A few possibilities I see are that I could be mistaken on what the routine actually is (the atari 800 manual wasn't very informative about that) or the push might point to something else other than the stack. I would say the latter is true but then we aren't pushing the character we are trying to print because of the txa instruction and the accumulator being reloaded.










share|improve this question









New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
    – dirkt
    3 hours ago










  • @dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
    – Raffzahn
    2 hours ago










  • @Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
    – dirkt
    1 hour ago










  • @dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
    – Raffzahn
    1 hour ago










  • @dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
    – Raffzahn
    1 hour ago














up vote
2
down vote

favorite












I am learning how to program the Atari 800 by examining some tutorial code that came with the IDE/Assembler I am using. I am using the MADS assembler for this.



putchar_ptr = $346
csrhinh = 752
character = $80
rowcrs = $54
colcrs = $55
org $2000

.proc main
mva #1 csrhinh
mva #6 rowcrs
mva #16 colcrs
mva #0 character

next_character
ldx character
cpx #.len text
beq stop
lda text,x
jsr putchar
inc character
jmp next_character

stop jmp stop

.proc putchar
tax
lda putchar_ptr+$1
pha
lda putchar_ptr
pha
txa
rts
.endp

.local text
.byte 'Hi there!',$9b,'new line'
.endl

.endp

run main


I did reference the Atari 800 manual and the MADS-Assembler manual but I didn't find anything. The specific question I am asking is, in the putchar procedure, why is the accumulator pushed onto the stack? From what I can tell all it is loaded with is the location of the routine pointer on the first push and the put pointer on the second. A few possibilities I see are that I could be mistaken on what the routine actually is (the atari 800 manual wasn't very informative about that) or the push might point to something else other than the stack. I would say the latter is true but then we aren't pushing the character we are trying to print because of the txa instruction and the accumulator being reloaded.










share|improve this question









New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
    – dirkt
    3 hours ago










  • @dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
    – Raffzahn
    2 hours ago










  • @Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
    – dirkt
    1 hour ago










  • @dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
    – Raffzahn
    1 hour ago










  • @dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
    – Raffzahn
    1 hour ago












up vote
2
down vote

favorite









up vote
2
down vote

favorite











I am learning how to program the Atari 800 by examining some tutorial code that came with the IDE/Assembler I am using. I am using the MADS assembler for this.



putchar_ptr = $346
csrhinh = 752
character = $80
rowcrs = $54
colcrs = $55
org $2000

.proc main
mva #1 csrhinh
mva #6 rowcrs
mva #16 colcrs
mva #0 character

next_character
ldx character
cpx #.len text
beq stop
lda text,x
jsr putchar
inc character
jmp next_character

stop jmp stop

.proc putchar
tax
lda putchar_ptr+$1
pha
lda putchar_ptr
pha
txa
rts
.endp

.local text
.byte 'Hi there!',$9b,'new line'
.endl

.endp

run main


I did reference the Atari 800 manual and the MADS-Assembler manual but I didn't find anything. The specific question I am asking is, in the putchar procedure, why is the accumulator pushed onto the stack? From what I can tell all it is loaded with is the location of the routine pointer on the first push and the put pointer on the second. A few possibilities I see are that I could be mistaken on what the routine actually is (the atari 800 manual wasn't very informative about that) or the push might point to something else other than the stack. I would say the latter is true but then we aren't pushing the character we are trying to print because of the txa instruction and the accumulator being reloaded.










share|improve this question









New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I am learning how to program the Atari 800 by examining some tutorial code that came with the IDE/Assembler I am using. I am using the MADS assembler for this.



putchar_ptr = $346
csrhinh = 752
character = $80
rowcrs = $54
colcrs = $55
org $2000

.proc main
mva #1 csrhinh
mva #6 rowcrs
mva #16 colcrs
mva #0 character

next_character
ldx character
cpx #.len text
beq stop
lda text,x
jsr putchar
inc character
jmp next_character

stop jmp stop

.proc putchar
tax
lda putchar_ptr+$1
pha
lda putchar_ptr
pha
txa
rts
.endp

.local text
.byte 'Hi there!',$9b,'new line'
.endl

.endp

run main


I did reference the Atari 800 manual and the MADS-Assembler manual but I didn't find anything. The specific question I am asking is, in the putchar procedure, why is the accumulator pushed onto the stack? From what I can tell all it is loaded with is the location of the routine pointer on the first push and the put pointer on the second. A few possibilities I see are that I could be mistaken on what the routine actually is (the atari 800 manual wasn't very informative about that) or the push might point to something else other than the stack. I would say the latter is true but then we aren't pushing the character we are trying to print because of the txa instruction and the accumulator being reloaded.







assembly 6502 atari-800






share|improve this question









New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 10 mins ago









Raffzahn

38.6k486155




38.6k486155






New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 3 hours ago









user115898

13418




13418




New contributor




user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






user115898 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











  • The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
    – dirkt
    3 hours ago










  • @dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
    – Raffzahn
    2 hours ago










  • @Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
    – dirkt
    1 hour ago










  • @dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
    – Raffzahn
    1 hour ago










  • @dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
    – Raffzahn
    1 hour ago
















  • The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
    – dirkt
    3 hours ago










  • @dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
    – Raffzahn
    2 hours ago










  • @Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
    – dirkt
    1 hour ago










  • @dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
    – Raffzahn
    1 hour ago










  • @dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
    – Raffzahn
    1 hour ago















The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
– dirkt
3 hours ago




The routine does an indirect jump to the contents of putchar_ptr, because rts jump to the contents just pushed on the stack. I don't know why they are doing it that way instead of using a plain indirect jump.
– dirkt
3 hours ago












@dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
– Raffzahn
2 hours ago




@dirkt Because the address is possibly stored in routine-1 form and ment to be used that way. An indirect Jump would require a non decremented one, So this is faster than loading and incrementing it for use with an indirect jump.
– Raffzahn
2 hours ago












@Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
– dirkt
1 hour ago




@Raffzahn: So why don't they just store it in addr instead of addr-1 form? It was certainly common to do that, e.g. COUT on the Apple II, and IIRC also in the C64 ROM.
– dirkt
1 hour ago












@dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
– Raffzahn
1 hour ago




@dirkt Because it's part of a list of addresses - or better list of IOCB. The Atari OS is a bit more sophisticated compared with the C64. It is based on an abstract, file based IO system called CIO, which used IO controll blocks for each open file. For BASIC there is a lisst of 8 IOCB predefined. Each has at offset 6 a pointer with the function to be called for output ($34*6* here). For outputing the routine is just called with the IOCB number times $10 in X, so instead of a fixed address, above LDA use (IOCB_list+6,X) to fetch whatever pointer is needed and uses the RTS mechanic.
– Raffzahn
1 hour ago












@dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
– Raffzahn
1 hour ago




@dirkt Also, COUT itself is just a JMP (CSW), thus saving the need for an indirec jump in your own program. THis only works with a single pointer at a fixed location, not an indexed list of pointers or structures.
– Raffzahn
1 hour ago










1 Answer
1






active

oldest

votes

















up vote
5
down vote



accepted










That's a usual way to an indirect JSR with a 6502. The 6502 does not support indirect subroutine calls (*1), so it has to be done in software. Indirect subroutine calls are a usefull tool for function calls into OS/library functions which may change during runtime or by configuration - like when redirecting output to a different driver. By using a routine pointer for certain calls it's easy to overload/replace them by just changeing that pointer (*2)



Lacking the indirect call the 6502 needs to emulate an indirect subroutine call in software by calling a subroutine which in turn pushes the pointer onto the stack (high first) and then jumping there by 'returning' to it. Adds some cycles, but also preserves the flexibility (*3)



In detail it works like this



LDA ptr+1 * high byte of target routine pointer
PHA * push down the stack
LDA ptr * high byte of target routine pointer
PHA * push down the stack
rts * 'returning' to the address at TOS


The TAX/TXA around is just to preserve the parameter (character to be printed) aroud the stack handling code.



The NES-Dev Wiki offers a nice page about this topic.




Above routine is in itself a waste of time (23 cycles) and code (9 bytes) compared to a JSR pointing to an indirect jump. Just when the OS table is, like in this case, prepared for being executed using this, it will hold the routine addresses minus one, so an indirect jump won't work.




Further, I'm not so sure that just grabing the routine from IOCB 0 is a great idea. While it should work, as IOCB0 is usually associated with the screen, it's definitly fault resistant. It might be way better to go thru the CIO first.



(Caveat: My Atari knowledge is only small and rather rusty)




*1 - One of the few really missing instructions that could have been added rather easy. And a major hint that the 6502 wasn't designed with a general purpose CPU in mind, but rather a microcontroller with its fixed address locations, where such redirection is done during compile/linkage time.



*2 - Always keep in mind to use target address minus one, as RTS will increment the address before fetching the next instruction.



*3 - Some OS did speed up this by puting a JMP-opcode in front of every callable pointer, allowing a user programm to just JSRing via the pointer-1 address, greatly reducing the overhead to 3 cycles.






share|improve this answer






















    Your Answer







    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "648"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    noCode: true, onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );






    user115898 is a new contributor. Be nice, and check out our Code of Conduct.









     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fretrocomputing.stackexchange.com%2fquestions%2f8022%2fwhy-does-this-6502-code-push-a-function-address-onto-the-stack-before-calling%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    5
    down vote



    accepted










    That's a usual way to an indirect JSR with a 6502. The 6502 does not support indirect subroutine calls (*1), so it has to be done in software. Indirect subroutine calls are a usefull tool for function calls into OS/library functions which may change during runtime or by configuration - like when redirecting output to a different driver. By using a routine pointer for certain calls it's easy to overload/replace them by just changeing that pointer (*2)



    Lacking the indirect call the 6502 needs to emulate an indirect subroutine call in software by calling a subroutine which in turn pushes the pointer onto the stack (high first) and then jumping there by 'returning' to it. Adds some cycles, but also preserves the flexibility (*3)



    In detail it works like this



    LDA ptr+1 * high byte of target routine pointer
    PHA * push down the stack
    LDA ptr * high byte of target routine pointer
    PHA * push down the stack
    rts * 'returning' to the address at TOS


    The TAX/TXA around is just to preserve the parameter (character to be printed) aroud the stack handling code.



    The NES-Dev Wiki offers a nice page about this topic.




    Above routine is in itself a waste of time (23 cycles) and code (9 bytes) compared to a JSR pointing to an indirect jump. Just when the OS table is, like in this case, prepared for being executed using this, it will hold the routine addresses minus one, so an indirect jump won't work.




    Further, I'm not so sure that just grabing the routine from IOCB 0 is a great idea. While it should work, as IOCB0 is usually associated with the screen, it's definitly fault resistant. It might be way better to go thru the CIO first.



    (Caveat: My Atari knowledge is only small and rather rusty)




    *1 - One of the few really missing instructions that could have been added rather easy. And a major hint that the 6502 wasn't designed with a general purpose CPU in mind, but rather a microcontroller with its fixed address locations, where such redirection is done during compile/linkage time.



    *2 - Always keep in mind to use target address minus one, as RTS will increment the address before fetching the next instruction.



    *3 - Some OS did speed up this by puting a JMP-opcode in front of every callable pointer, allowing a user programm to just JSRing via the pointer-1 address, greatly reducing the overhead to 3 cycles.






    share|improve this answer


























      up vote
      5
      down vote



      accepted










      That's a usual way to an indirect JSR with a 6502. The 6502 does not support indirect subroutine calls (*1), so it has to be done in software. Indirect subroutine calls are a usefull tool for function calls into OS/library functions which may change during runtime or by configuration - like when redirecting output to a different driver. By using a routine pointer for certain calls it's easy to overload/replace them by just changeing that pointer (*2)



      Lacking the indirect call the 6502 needs to emulate an indirect subroutine call in software by calling a subroutine which in turn pushes the pointer onto the stack (high first) and then jumping there by 'returning' to it. Adds some cycles, but also preserves the flexibility (*3)



      In detail it works like this



      LDA ptr+1 * high byte of target routine pointer
      PHA * push down the stack
      LDA ptr * high byte of target routine pointer
      PHA * push down the stack
      rts * 'returning' to the address at TOS


      The TAX/TXA around is just to preserve the parameter (character to be printed) aroud the stack handling code.



      The NES-Dev Wiki offers a nice page about this topic.




      Above routine is in itself a waste of time (23 cycles) and code (9 bytes) compared to a JSR pointing to an indirect jump. Just when the OS table is, like in this case, prepared for being executed using this, it will hold the routine addresses minus one, so an indirect jump won't work.




      Further, I'm not so sure that just grabing the routine from IOCB 0 is a great idea. While it should work, as IOCB0 is usually associated with the screen, it's definitly fault resistant. It might be way better to go thru the CIO first.



      (Caveat: My Atari knowledge is only small and rather rusty)




      *1 - One of the few really missing instructions that could have been added rather easy. And a major hint that the 6502 wasn't designed with a general purpose CPU in mind, but rather a microcontroller with its fixed address locations, where such redirection is done during compile/linkage time.



      *2 - Always keep in mind to use target address minus one, as RTS will increment the address before fetching the next instruction.



      *3 - Some OS did speed up this by puting a JMP-opcode in front of every callable pointer, allowing a user programm to just JSRing via the pointer-1 address, greatly reducing the overhead to 3 cycles.






      share|improve this answer
























        up vote
        5
        down vote



        accepted







        up vote
        5
        down vote



        accepted






        That's a usual way to an indirect JSR with a 6502. The 6502 does not support indirect subroutine calls (*1), so it has to be done in software. Indirect subroutine calls are a usefull tool for function calls into OS/library functions which may change during runtime or by configuration - like when redirecting output to a different driver. By using a routine pointer for certain calls it's easy to overload/replace them by just changeing that pointer (*2)



        Lacking the indirect call the 6502 needs to emulate an indirect subroutine call in software by calling a subroutine which in turn pushes the pointer onto the stack (high first) and then jumping there by 'returning' to it. Adds some cycles, but also preserves the flexibility (*3)



        In detail it works like this



        LDA ptr+1 * high byte of target routine pointer
        PHA * push down the stack
        LDA ptr * high byte of target routine pointer
        PHA * push down the stack
        rts * 'returning' to the address at TOS


        The TAX/TXA around is just to preserve the parameter (character to be printed) aroud the stack handling code.



        The NES-Dev Wiki offers a nice page about this topic.




        Above routine is in itself a waste of time (23 cycles) and code (9 bytes) compared to a JSR pointing to an indirect jump. Just when the OS table is, like in this case, prepared for being executed using this, it will hold the routine addresses minus one, so an indirect jump won't work.




        Further, I'm not so sure that just grabing the routine from IOCB 0 is a great idea. While it should work, as IOCB0 is usually associated with the screen, it's definitly fault resistant. It might be way better to go thru the CIO first.



        (Caveat: My Atari knowledge is only small and rather rusty)




        *1 - One of the few really missing instructions that could have been added rather easy. And a major hint that the 6502 wasn't designed with a general purpose CPU in mind, but rather a microcontroller with its fixed address locations, where such redirection is done during compile/linkage time.



        *2 - Always keep in mind to use target address minus one, as RTS will increment the address before fetching the next instruction.



        *3 - Some OS did speed up this by puting a JMP-opcode in front of every callable pointer, allowing a user programm to just JSRing via the pointer-1 address, greatly reducing the overhead to 3 cycles.






        share|improve this answer














        That's a usual way to an indirect JSR with a 6502. The 6502 does not support indirect subroutine calls (*1), so it has to be done in software. Indirect subroutine calls are a usefull tool for function calls into OS/library functions which may change during runtime or by configuration - like when redirecting output to a different driver. By using a routine pointer for certain calls it's easy to overload/replace them by just changeing that pointer (*2)



        Lacking the indirect call the 6502 needs to emulate an indirect subroutine call in software by calling a subroutine which in turn pushes the pointer onto the stack (high first) and then jumping there by 'returning' to it. Adds some cycles, but also preserves the flexibility (*3)



        In detail it works like this



        LDA ptr+1 * high byte of target routine pointer
        PHA * push down the stack
        LDA ptr * high byte of target routine pointer
        PHA * push down the stack
        rts * 'returning' to the address at TOS


        The TAX/TXA around is just to preserve the parameter (character to be printed) aroud the stack handling code.



        The NES-Dev Wiki offers a nice page about this topic.




        Above routine is in itself a waste of time (23 cycles) and code (9 bytes) compared to a JSR pointing to an indirect jump. Just when the OS table is, like in this case, prepared for being executed using this, it will hold the routine addresses minus one, so an indirect jump won't work.




        Further, I'm not so sure that just grabing the routine from IOCB 0 is a great idea. While it should work, as IOCB0 is usually associated with the screen, it's definitly fault resistant. It might be way better to go thru the CIO first.



        (Caveat: My Atari knowledge is only small and rather rusty)




        *1 - One of the few really missing instructions that could have been added rather easy. And a major hint that the 6502 wasn't designed with a general purpose CPU in mind, but rather a microcontroller with its fixed address locations, where such redirection is done during compile/linkage time.



        *2 - Always keep in mind to use target address minus one, as RTS will increment the address before fetching the next instruction.



        *3 - Some OS did speed up this by puting a JMP-opcode in front of every callable pointer, allowing a user programm to just JSRing via the pointer-1 address, greatly reducing the overhead to 3 cycles.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 49 mins ago









        α CVn

        1,3811825




        1,3811825










        answered 3 hours ago









        Raffzahn

        38.6k486155




        38.6k486155




















            user115898 is a new contributor. Be nice, and check out our Code of Conduct.









             

            draft saved


            draft discarded


















            user115898 is a new contributor. Be nice, and check out our Code of Conduct.












            user115898 is a new contributor. Be nice, and check out our Code of Conduct.











            user115898 is a new contributor. Be nice, and check out our Code of Conduct.













             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fretrocomputing.stackexchange.com%2fquestions%2f8022%2fwhy-does-this-6502-code-push-a-function-address-onto-the-stack-before-calling%23new-answer', 'question_page');

            );

            Post as a guest













































































            Comments

            Popular posts from this blog

            Long meetings (6-7 hours a day): Being “babysat” by supervisor

            Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

            Confectionery