Jump to content
IGNORED

Find own address in 6502 assembler?


Recommended Posts

Is it possible for a 6502 program to find out it's own address? I programmed a ML monitor/disassembler/mini-assembler ages ago which I'd like to "revive" and implement bank switching (it was done on a plain 800). Further I'd like to be able to load it at any address and then relocate itself without having the load address as a parameter (currently there is a small loader that loads the rest of the code and does the relocation stuff but if possible I'd like to fit it all in one program.

 

Thanks!

 

Link to comment
Share on other sites

In the assembly process itself, the system variable "*" is like the program counter which tracks where assembly is at.

 

e.g.

 

  *=$600
  jmp start3
start .word *
start2 .word *
start3 lda #1
  ldx #<start3
  ldy #>start3

 

should assemble 2 x 16-bit low-endian words, 1st contains $0603, 2nd contains $0605. When that program runs, X and Y will contain the address of the label "start3".

 

That sort of thing isn't useful if it's relocatable code though, although relocatable code isn't something 6502 does very well.

If you have a relocatable program then to find where in memory you reside, easiest way would probably be to use a dummy subroutine call.

 

 

  jsr getmyaddress
getmyaddress  pla
  tay
  pla
  tax
  inx
  bne  dontinc
  iny
dontinc

 

The "dummy subroutine" pulls the return address from the stack. The JSR instruction actually pushes the address of the last byte of the operand which is why we increment it (I think the X/Y stuff is the right way around here, if it doesn't work then swap the TAX/TAY around).

So, X/Y in this case should contain the address of "getmyaddress" - if required you can adjust to point to elsewhere relative to that label.

Link to comment
Share on other sites

Well, the idea is that you'd set up a global subroutine at a known address which modules could call to find out their address. :) But I don't think I follow the described scenario anyway. The source address of the code has to be known one way or another in advance by the loader in order to load the code into RAM in the first place. After that, it's a question of finding the difference between the source (hard-coded in the executable file) and target address and relocating to suit.

Link to comment
Share on other sites

Oops... the subroutine actually should be fixed in memory. So would need to be a real sub and seperate entity from a relocatable section.

 

Better way would obviously be by examining the stack, that way you can RTS cleanly.

 

If it was an Atari-specific program you could probably get tricky and just use a BRK instruction then examine the stack. Probably have to disable hw interrupt sources first though.

Edited by Rybags
Link to comment
Share on other sites

I thought about JSR and pulling the stack, too, but that needs a fixed place to jump to. OTOH, if I want to be able to put the main program in any bank, I'll need some "always visible" home for the switching routine anyhow, so I could put the relocator there.

 

Would it be an option to read some DOS or CIO variable to find where the program was loaded to?

Link to comment
Share on other sites

What's this reference to "bank" ?

 

If you're talking 130XE type banks then the address range is always $4000-$7FFF - the bank number that's stored to PORTB can be the variable thing. It's the application's responsibility in that case to do the memory management there, and then you have to avoid conflict with DOS Ramdisk if it's in use.

 

CIO doesn't really have the concept of "load program" - it just treats things as Get/Put operations on data.

 

How is it that your program can come to be loaded at some random location to begin with?

 

I think for the self-supported use of finding where in memory the program is, the BRK method might be the way to do it.

Link to comment
Share on other sites

What's this reference to "bank" ?

 

If you're talking 130XE type banks then the address range is always $4000-$7FFF - the bank number that's stored to PORTB can be the variable thing. It's the application's responsibility in that case to do the memory management there, and then you have to avoid conflict with DOS Ramdisk if it's in use.

 

CIO doesn't really have the concept of "load program" - it just treats things as Get/Put operations on data.

 

How is it that your program can come to be loaded at some random location to begin with?

 

I think for the self-supported use of finding where in memory the program is, the BRK method might be the way to do it.

I admit I have to learn about bank switching from the start as that wasn't around when I learned assembly programming my Atari.

 

As for the random address, my idea was to be able to load the program at any address and then run it from there (to allow the monitor to be loaded to a convenient location).

 

I assume for the BRK method I'd have to set an interrupt vector to the code doing the relocation, use BRK to interrupt, then read the stack and then pass the address on to the relocation routine. Which would still need a "fixed part" for the BRK to trap to? The more I think about this, the rustier I feel....

Link to comment
Share on other sites

CIO doesn't really have the concept of "load program" - it just treats things as Get/Put operations on data.

 

Should have known that....and even if DOS stores it somewhere that would probably be in a different location for every DOS....

Link to comment
Share on other sites

Relocatable code isn't practical for much other than short/simple programs.

 

Or are you intending to have the program able to relocate itself?

 

Also, I think there's relocatable program ability for SpartaDos - maybe someone else can explain that further.

 

The BRK method I described - there's 2 ways to go about it, either use the vector then have the routine pass back the call address.

Alternate to that, leave the vector as is, execute BRK and examine the stack. All other interrupt sources would need to be suppressed so not to corrupt the entry while you examine it though.

Link to comment
Share on other sites

SDX has its own relocatable format, and relocatable programs using that DOS can be as big as you want them to be. Am I to assume the requirement here is for code to figure out where it lives after it's been relocated? Because otherwise, I'm still at a loss to know how the loader would have trouble knowing where in memory the code was located prior to relocation.

Link to comment
Share on other sites

You could JSR into the P: initialization code in the OS, it only sets the printer timeout variable then does RTS so is minimalistic and guaranteed to not move.

 

jsr $e43c

 

You still need to ensure no interrupts occur though as the stack can get overwritten, something like this:

 

 

  lda #0
  sta nmien
  sei
  jsr $e43c
; now examine stack
;
  cli
  lda #$40
  sta nmien
  • Like 1
Link to comment
Share on other sites

Just a matter of style, I would use the lower area of the stack page. I've never seen the stack go below ~7 deep/14 bytes so you would have 200 bytes to play with. If you put your code there you don't have to worry about interrupts or the stack. You didn't say how the program will find where it wants to go though. I mean does it get parameters passed to it like Sparta or examines memlo, that kind of thing.

 

I would do it as DOS initiate code and force the mainline to locate on page boundary to keep it simple. You would only have to adjust the high byte of the variables that way and no roll over you get by increment $4FFF to $5000 where you would have to do 16 bit addition. Much shorter IMHO to just go $4F to $50 and the $FF stays the same.

 

It could be done a number of different ways. Everything from having the code continue the DOS load process to being something your program just jsr's to to find out where it lives. You could also just have a single file that transfers the code from within it to page 1 or 6 for that matter then jsr's to it.

Link to comment
Share on other sites

Hello,

 

About relocatable code, a table of all the instances of absolute addresses (entry points and data) should to be generated by the assembler, something with the label and the offset from the begin of the program. For each program, two parts should be managed, the table and the program assembled to start with *=0. The loader (a part of the OS/DOS) should load the program at the lowest free address and add the correct offset to each instance of absolute addresses.

 

But... There are different segments for each program, for instance page zero segment has to be manage in the same way as the code itself... It could be quickly becoming very hard. The I/O registers must not be managed in the same way, they are real absolute addresses, and it not easy to share them with 2 or more programs. I think that making a patch in the OS/DOS and writing an assembler with some more instruction could be a very huge task.

 

In BASIC, you can put an assembler routines in a string, and call it. You can add as much strings/routines as you want, the BASIC can retrieve the location of each string and invoke an USR command if you want. There is no need to know the absolute address of the routine. But these routines have to be simple, without external data (or absolute address data). Temporary data has to be managed with the stack.

 

Link to comment
Share on other sites

Several well known programs do this kind of thing, but not from a cartridge let alone a banked cartridge. Banking scheme was there from day one, you and I were just not aware of it at that time. Super Arc, Super Unarc, Disk Communicator (DCM utility) and Bug/65 all load into high memory from disk and self relocate to MEMLO (or in Bug's case where you optionally can tell it to go and run at) except for Sparta loads and then they relocate to MEMLO plus a couple pages at least for Puff's utilities. Disassemble them and see how to go about it? I'm reminded of the guy trying to re-invent the wheel almost, but I'll not say any more about it since it's your code and you are going to do what you want with it anyway. The idea being to have fun I suspect too. So have fun.

Link to comment
Share on other sites

Several well known programs do this kind of thing, but not from a cartridge let alone a banked cartridge. Banking scheme was there from day one, you and I were just not aware of it at that time. Super Arc, Super Unarc, Disk Communicator (DCM utility) and Bug/65 all load into high memory from disk and self relocate to MEMLO (or in Bug's case where you optionally can tell it to go and run at) except for Sparta loads and then they relocate to MEMLO plus a couple pages at least for Puff's utilities. Disassemble them and see how to go about it? I'm reminded of the guy trying to re-invent the wheel almost, but I'll not say any more about it since it's your code and you are going to do what you want with it anyway. The idea being to have fun I suspect too. So have fun.

Had that "re-invent the wheel" thoughts back in the 80s when I learned about BUG/65 and DDT but both of those were still out of reach when I did my original code with MAE, having taken first steps into assembly with the Monkey Wrench II (sadly no longer in my possession) before. You're right, it's more about having fun and accessing my youth than about creating brand new useful functionality.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...