Why does this 6502 code push a function address onto the stack before calling?
Clash 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.
assembly 6502 atari-800
New contributor
 |Â
show 1 more comment
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.
assembly 6502 atari-800
New contributor
The routine does an indirect jump to the contents ofputchar_ptr
, becauserts
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 inaddr
instead ofaddr-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
 |Â
show 1 more comment
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.
assembly 6502 atari-800
New contributor
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
assembly 6502 atari-800
New contributor
New contributor
edited 11 mins ago
Raffzahn
38.6k486155
38.6k486155
New contributor
asked 3 hours ago
user115898
13418
13418
New contributor
New contributor
The routine does an indirect jump to the contents ofputchar_ptr
, becauserts
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 inaddr
instead ofaddr-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
 |Â
show 1 more comment
The routine does an indirect jump to the contents ofputchar_ptr
, becauserts
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 inaddr
instead ofaddr-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
 |Â
show 1 more comment
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 JSR
ing via the pointer-1 address, greatly reducing the overhead to 3 cycles.
add a comment |Â
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 JSR
ing via the pointer-1 address, greatly reducing the overhead to 3 cycles.
add a comment |Â
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 JSR
ing via the pointer-1 address, greatly reducing the overhead to 3 cycles.
add a comment |Â
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 JSR
ing via the pointer-1 address, greatly reducing the overhead to 3 cycles.
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 JSR
ing via the pointer-1 address, greatly reducing the overhead to 3 cycles.
edited 49 mins ago
ñ CVn
1,3811825
1,3811825
answered 3 hours ago
Raffzahn
38.6k486155
38.6k486155
add a comment |Â
add a comment |Â
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.
user115898 is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
The routine does an indirect jump to the contents of
putchar_ptr
, becauserts
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 ofaddr-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