6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Oct 05, 2024 10:03 pm

All times are UTC




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Mon Mar 12, 2018 6:43 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 588
Location: Michigan, USA
The excerpt below is from the July 1982 issue of Apple Assembly Lines. I understand what the code is doing but I can't figure out the context. That is, when would you need to use this particular call & return method?

TIA... Cheerful regards, Mike

Quote:
Run-Anywhere Subroutine Calls Bob Sander-Cederlof

Bob Nacon (author of Amper-Magic) called yesterday and told me about his new way to call subroutines in programs that will be loaded anywhere in memory without relocation or reassembly. He does this a lot inside Amper-Magic, and you might want to do it yourself sometime.

Instead of JSR <subroutine name>, put the following three lines whenever you call a subroutine:

CLV
JSR $FF58
BVC <subroutine name>

The byte at $FF58 in the monitor ROM is always $60, an RTS instruction. Since this is used by most Apple interface boards, Apple has guaranteed that it will always be $60. The JSR to a guaranteed RTS instruction seems silly, doesn't it? Not quite, because it does put two bytes on the stack, and then pop them off again. But we can get them back later, inside the called subroutine, like this:

TSX GET STACK POINTER
DEX
DEX
TXS REVISED STACK POINTER

Now the subroutine we called has a return address to go to, just as though we had used JSR <subroutine name>! The only problem is that if we execute an RTS, we will re-execute the BVC <subroutine name> and be in a loop. Unless....

Unless we set overflow, so the BVC falls through. But there is no SEV opcode in the 6502, so what do we do? $FF58 to the rescue again! Here is how we end the subroutine:

BIT $FF58 SET OVERFLOW
RTS

The BIT instruction copies bit 7 of $FF58 into the N-status bit, and bit 6 into the Overflow status bit. This, in other words (since $FF58 has $60 in it) clears N and sets Overflow. BIT does not affect Carry Status in any way. BIT also sets or clears the Z-status bit, according to the value of the logical product of the A-register and the addressed byte. If you want Z and/or N to be flags to the calling program, you will have to modify them after the BIT instruction.

I thank Bob Nacon for this technique, and he thanks Roger Wagner for putting him on the trail to its discovery. Roger writes the monthly column in Softalk Magazine called "Assembly Lines"; the December, 1981, issue covered writing run-anywhere programs. If you haven't got Roger's book yet, called "Assembly Lines: The Book", it is currently the best book for beginners that I know of. The regular price is $19.95+$2 shipping, but I sell them for $18+$2 shipping.


Last edited by Michael on Sat Mar 17, 2018 1:22 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 7:26 pm 
Offline

Joined: Sat Aug 19, 2017 1:42 pm
Posts: 35
Location: near Karlsruhe, West-Germany
Michael wrote:
Bob Nacon (author of Amper-Magic) called yesterday and told me about his new way to call subroutines in programs that will be loaded anywhere in memory without relocation or reassembly. He does this a lot inside Amper-Magic, and you might want to do it yourself sometime.

Instead of JSR <subroutine name>, put the following three lines whenever you call a subroutine:

CLV
JSR $FF58
BVC <subroutine name>
The byte at $FF58 in the monitor ROM is always $60, an RTS instruction.

Assume you want to write some code which is placed in a firmware PROM on a Applebus board. You don't know in which slot this board will be. In slot #1 the code starts at $C100, in slot #2 at $C200, ... If you want to call a subroutine you cannot use a simple "jsr $C1xx".

This is just a tricky method to do this.

Regards
Ralf


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 8:32 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8521
Location: Southern California
Just make sure an interrupt (including NMI) won't overwrite that return address. (The 6502 stacks treatise has whole chapters on these things.)

_________________
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  
PostPosted: Mon Mar 12, 2018 9:04 pm 
Offline

Joined: Sat Aug 19, 2017 1:42 pm
Posts: 35
Location: near Karlsruhe, West-Germany
GARTHWILSON wrote:
Just make sure an interrupt (including NMI) won't overwrite that return address.

That's right. But in the early 1980s the world of Apple didn't use IRQs. Ok, a NMI could not be blocked. But the risk was manageable in ~15 cycles.

If I had to write some code at this time I would have used such tricky things just for booting the operating system.

Regards
Ralf


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 10:17 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
This method can be better optimized, by eliminating the stack fixup, and increasing safety by leaving the return address on the stack:

Code:
  CLV
  JSR *+3  ;jsr to the next instruction, leaving the PC on the stack
  BVC subroutine
  ...

subroutine:
  ...work...
  BIT $ff58  ; set V
  RTS


This is basically implementing a short relative JSR, instead of an absolute JSR. However, since it only has the signed 8-bit range, I presume it would often have to be paired with another 16-bit jump, which would kind of defeat the purpose.

If you wanted to get even more hackish, you could use the JSR to put PC on the stack, manually add a signed 16-bit delta, and RTS to it for a long relative JMP/JSR.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Last edited by White Flame on Mon Mar 12, 2018 10:28 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 10:28 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10949
Location: England
I don't think you can JSR *+3, as you don't know what * is.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 10:29 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
And that's why I shouldn't post when I'm tired. ;)

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 10:32 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 588
Location: Michigan, USA
Duh! Just figured it out.

Say you have 200 bytes of some neat utility code that includes a subroutine. Where a JSR instruction utilizes an absolute address, the branch instruction uses relative addressing (relative to the program counter) and so this method allows me to load and run the code from almost any address. Very cool! The limitation is that the subroutine would need to be up to 127 bytes forward or up to 128 bytes backward from the program counter.

Thanks, guys. Cheerful regards, Mike


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 12, 2018 10:33 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10949
Location: England
I wonder if there's a collection somewhere of tricks and techniques for position-independent code on the 6502...


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 13, 2018 12:27 am 
Offline

Joined: Sat Aug 19, 2017 1:42 pm
Posts: 35
Location: near Karlsruhe, West-Germany
BigEd wrote:
I wonder if there's a collection somewhere of tricks and techniques for position-independent code on the 6502...

I don't know a collection, but I saw some in the ROMs of my Apple IIe and in the BIOS and interpreter of the UCSD p-system when I did my analysis in the 1980s as a student.

I remember once more:
- push the return address onto stack (i.e. add the slot number to $C0 and push that)
- push the address of the subroutine onto the stack (if suitable push the same $C0+s onto stack)
- run rts to call the subroutine
- run rts to come back
Again: no IRQs, no NMIs.

Another one:
Use an address table which is originally generated with relative addresses and when installing in RAM the absolute addresses are computed by adding the offset to each entry. That's ok for modules which are used for a long time and are not changed every millisecond. jmp (ind) or jmp (ind,X) (both CMOS) are valuable :-)

Still another one:
AFAIR the UCSD code used that, and me too: code which is modified by the code if code runs from RAM.

I also generated mid-distance jumps without absolute addressing of the jmp instruction by generating several bra (CMOS) like a daisychain.

Regards
Ralf


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 13, 2018 8:17 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
RalfK wrote:
AFAIR the UCSD code used that, and me too: code which is modified by the code if code runs from RAM.
https://en.wikipedia.org/wiki/Dynamic_linker

_________________
6502 sources on GitHub: https://github.com/Klaus2m5


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

All times are UTC


Who is online

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