6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat May 18, 2024 12:36 am

All times are UTC




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Feb 02, 2003 8:39 pm 
Offline

Joined: Sun Feb 02, 2003 8:19 pm
Posts: 2
Lets say, hypothetically speaking, a program(in java) has to be created to take any 6502 program and "move" it to a specified position in memory(which is taken in as a hexidecimal 4 digit string). How would i know which commands have imbedded address that i must change, and which ones must remain static.

For example, in the source code repository, a MicroChess program is available(by Peter Jennings). The first few lines of the program include:

ACIADat = $7F70
ACIASta = $7F71

Would these values have to be changed if i moved the entire program in memory? for example, if the user inputted 1EE6, would this code segment now equal:

ACIADat = $9E56
ACIASta = $9E57

Some other questionable code segments include(in a random order) include:

MAXC = $E4
CMP #$43
LDA #$CC
LDY #$0F
ADC #$90

I have no idea how to tell if any certain hex value must be relocated or not... In the Turorials and Aids section of this webpage, there is a 6502 Opcodes documentation available by John Pickens. A sample of some information in there includes:

ADC (ADd with Carry)
Affects Flags: S V Z C

MODE SYNTAX HEX LEN TIM
Immediate ADC #$44 $69 2 2
Zero Page ADC $44 $65 2 3
Zero Page,X ADC $44,X $75 2 4
Absolute ADC $4400 $6D 3 4
Absolute,X ADC $4400,X $7D 3 4+
Absolute,Y ADC $4400,Y $79 3 4+
Indirect,X ADC ($44,X) $61 2 6
Indirect,Y ADC ($44),Y $71 2 5+

+ add 1 cycle if page boundary crossed

I am not sure how to interpret these, but i realize embedded within may be my answer. Any help on which commands have to be relocated and which ones don't, or any links to any documentation that could help me in my search would be greatly appreciated, thanks..

_________________
_-Revrick Bloodthirsty-_

--Unity is key..


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 03, 2003 4:17 am 
Offline

Joined: Wed Sep 04, 2002 4:08 pm
Posts: 57
Location: Iowa
Hopefully someone with a little more 6502 experience than me can help you, but I'll offer my insight.

If you have assember source code for the program, it can be as simple as changing the program location counters, which in the source code look like *=xxxx or .ORG xxxx where xxxx is the starting location. In the simplest case, there'll be only one at the start of the source to state the "origin" of the program, or the address of the first instruction to assemble.

If there's more than one location counter directive, you'll have to decide whether to offset them by the same amount or not. For example, if the program has the reset vectors in it, there will probably be a directive to set the location counter to $FFFA; don't change this.

Let's talk about program code before data.

It's a little unclear from your example whether or not the source code you wish to relocate is using labels or hard-coded addresses. If it is using labels, again you will wish to offset the program location counter directives.

If it is using hard coded addresses, there are certain instructions which refer to program locations; such as JSR, JMP, and the branch instructions. Adjust JSR and JMP, but not branch instructions (they are offsets to begin with). Now that I think of it, you'll want to be careful with JSR, too--some JSRs will jump to subroutines within your program. Others will jump to fixed system-call locations. You may be able to tell which are which by whether or not the jump is within the bounds of the program.

Now for data. I'll assume from your example that you wish to leave all data where it is. If so, do not adjust instructions that refer to it, such as LDA, ADC, etc. (You will need to learn the instruction set if you don't know it, to tackle this problem; it's an easy set to learn). Again, there may be hard coded addresses or labels of the form "label=xxxx". A third type of data you'll see is a reservation for data space, such as "label .BYTE xx". There's nothing to change here; they'll move as the program location counter moves.

If you wish to relocate data, you have a difficult task ahead of you, because there'll be many decisions to be made, mostly on the context of the data (is it stack? is it zero page? is it hardware access? is it a fixed location in RAM such as a screen buffer?).

You do not want to adjust lines like ACIA=xxxx. These no doubt refer to hardware registers whose locations don't normally change. You will need to have knowledge of your target platform to know what exact address to change these to if necessary, but you'll never want to offset them to coincide with the offset of the program code. You'll also want to ensure that the program is not using hardware that is unavailable on your target (for example, a program written to use a 6551 UART where none is available). Otherwise a rewrite of input/output code is necessary.

Stack access (from $0100 to $01FF) should not be adjusted. An example is LDA $0100,X; don't adjust $0100. Zero-page must generally be kept within zero-page, as the indirect instructions only work with zero-page. Everything else is a per-data-item judgement call.

If you have nothing more than binary code (no source), you're out of luck for all practical purposes. At best, you could disassemble the binary and adjust any addresses as per the suggestions above, but one problem is that you may not know which binary that you've disassembled is actual 6502 instructions, and which binary is initialization or constant data. For example, a string in binary that gives the program's help page or text prompt or introduction banner should not be adjusted, but will probably disassemble into (meaningless) 6502 code. How are you to know which is which, much less an automated relocator program?

Hand editing source code with knowledge of both the originally intended platform and the target you wish to port the program to is probably the only reliable way to go.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Feb 04, 2003 2:25 am 
Offline

Joined: Wed Jan 22, 2003 6:54 am
Posts: 56
Location: Estados Unidos
Hi. Why would you need to do this relocation?

_________________
Thanks for playing.
-- Lord Steve


Top
 Profile  
Reply with quote  
 Post subject: why oh why...
PostPosted: Wed Feb 05, 2003 2:26 am 
Offline

Joined: Sun Feb 02, 2003 8:19 pm
Posts: 2
This relocation project was assigned by my professor.. We were to use any means neccessary to develop a working understanding of 6502 relocation.. btw, thank you schidester, your post was very informative..

_________________
_-Revrick Bloodthirsty-_

--Unity is key..


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Feb 05, 2003 3:54 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8438
Location: Southern California
schiedester did indeed do a very good job of answering.  But if the prof ordered it, now I wonder if either the code to relocate was rather simple (since he can't expect you to work on it 40 hours a week or more for months), or if the intent was for the student to find out the hard (but educational) way that it's not feasible (at least not without the source code).  6502 code could be written to be relocatable (such that it could be loaded at different starting addresses without re-assembling), but it's pretty impractical.  The 65816 lends itself to this much better.

_________________
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  
 Post subject:
PostPosted: Wed Feb 05, 2003 4:28 pm 
Offline

Joined: Wed Sep 04, 2002 4:08 pm
Posts: 57
Location: Iowa
Revrick,

What an unusual assignment! I had another thought or two.

If you're expected to relocate object code as a class exersize, you must have been given a program that is indeed easy to relocate. I'd say again that once you've developed a basic understanding of 6502 assembly, you could tackle the problem by offsetting any address operands that refer to addresses within the bounds of the original program. For example, if you have a program that normally occupies $1000 to $2000, then any JMP or JSR within that space should be adjusted; also anything like LDA $1020 (using "direct" addressing mode).

If you're expected to write a fully relocatable program, then get ready for some tedious work; the only straightforward way I can think to do it is to use only branch instructions which modify the program counter relative to the current location; in other words, don't use JMP or JSR at all (JSR for OS calls is fine). Unfortunately these are limited to about 127 byte jumps in either direction.

Garth, I have a question since I have no 65816 experience, but am looking forward to working with one. Can 65816 code be relocated arbitrarily or only on 64K boundaries? Are there relative JMP and JSR instructions?


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 05, 2003 9:04 pm 
Offline
Site Admin
User avatar

Joined: Fri Aug 30, 2002 1:08 am
Posts: 280
Location: Northern California
GARTHWILSON wrote:
6502 code could be written to be relocatable (such that it could be loaded at different starting addresses without re-assembling), but it's pretty impractical. The 65816 lends itself to this much better.


Andre Fachat, whose pages are hosted on 6502.org, invented the O65 file format for relocatable 6502 and 65816 binaries. You can find more information about it at: http://www.6502.org/users/andre/o65/index.html .

Regards,
Mike

_________________
- Mike Naberezny (mike@naberezny.com) http://6502.org


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Feb 06, 2003 2:09 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8438
Location: Southern California
Thanks Mike for pointing out Andre Fachat's work.  He has done an impressive job of devising an operating system that can handle multiple programs in memory at the same time.  I definitely should take time to look at it in more detail.

There is one thing I did not see at the website, although Mr. Fachat may be doing it—that is, addressing the possibility of truly relocatable 6502 code, such that the program does not need extra tables (except for other programs to use parts of it) or any special considerations at load time.  It's not efficient, but handling long relative addresses can in fact be done on a 6502 because the JSR instruction puts its own last byte's address on the stack.  This means that regardless of where the program got loaded, the run-time address of a given part can be known and used for adding an offset to it, getting addresses for data or long branching even though the absolute address information was not known at the time of assembly and the code was not modified at the time of loading.

The JSR itself does not use a relative address, so that address can't move around; but the same routines that calculate addresses at run time could be used by several programs that got loaded into memory in any order.  This may be part of Mr. Fachat's OS—I don't know.  As you can see, the JSR will not always be used for the usual calling of a subroutine—in fact, you might use RTS to do a call or a jump, after the calculated address (minus one) is put on the stack.

I have toyed with this idea some, but I cannot say I've been using it.  And while I have used the 65816 some, the attractions I have taken advantage of are primarily the added ease of handling 16-bit numbers and many of the new instructions and addressing modes.  I have not specifically taken advantage of its resources for facilitating relocatable programs or for using a 16MB address space.

Quote:
Garth, I have a question since I have no 65816 experience, but am
looking forward to working with one. Can 65816 code be relocated
arbitrarily or only on 64K boundaries? Are there relative JMP and JSR
instructions?

With that said, schidester, understand that my information comes from the WDC programming manual, and that some of the extra instructions I mention are ones that I have only used for reasons other than writing relocatable code.

So we have (and this is definitely not an exhaustive list):
  • PEA: push 16-bit immediate data onto stack (without affecting processor registers)
  • PEI:  like a 16-bit LDA from direct page, then PHA, but without affecting processor registers
  • PER: Add the current (run-time) program counter value to the 16-bit signed displacement in the operand, and push the result on the stack.  The program counter value used is the one effective at run time, and could be different every time the program is loaded.
  • BRL: branch relative always long

There is no BSR (branch to subroutine) or JSR-relative-long instruction, but it can be easily synthesized just by using PER return-address-minus-one, BRL (branch relative long).  In this case you actually have the advantage that you can specify the return address relative to the calling address, and you don't necessarily have to return to the very next byte or instruction.  An example of its usefulness would be where the next several bytes are data to be used by the subroutine.  When the subroutine is done, you don't want the program counter to come back and start trying to execute bytes that are data and not instructions.  I've done this kind of thing with the 65c02, but the 65816's new instructions would have made it much easier and far more efficient.

For another example, consider the situation where you want to pass several parameters to a subroutine via the stack (good for re-entrancy or recursion), and have it return with the stack already cleaned up.  To make the clean-up easier, you can use PER to first push the return address, then use PER or PEA (in addition to the usual PHA etc.) to push parameters or their relative addresses onto the stack, and then do the actual subroutine call with a BRL.

There is also a JSR(addr,X), ie, absolute indexed indirect.  Remember X can be 16-bit.

If you push an address onto the stack as a parameter to pass to another routine, you can use instructions like ADC (sr,S),Y (ie, stack-relative indirect, indexed by Y).  In essence, you can tell it something like, "get the 5th and 6th bytes on the stack, read the address pointed to there, add Y (which may be 16 bits) to it, get the 8- or 16-bit data contained at the resulting address, and add it to the accumulator's contents."  If you're doing a 16-bit operation, it takes 8 clocks.  Otherwise it takes 7.

The stack-relative addressing modes make it easier to use the stack for parameter-passing.  Especially with 65816's 16-bit stack pointer, you don't have to worry about running out of stack space.

To dispel some common misconceptions about the 65816  [Edit, 20 years later:  I have a more complete article about that, here]:
  1. I sense that it intimidates a lot of 6502 fans; but the '816 is really not hard to get into.  You can initially start using it virtually the same as a 6502, and begin taking advantage of its extra capabilities as you learn them.

  2. You don't always have to handle 16 bits at a time.  The accumulator can be 8 or 16 bits, and for some purposes is viewed as two 8-bit accumulators.  X and Y can be either 8- or 16-bit, although you cannot have X set to 8-bit and Y set to 16-bit (or vice-versa) at the same time.  Whether A or X&Y are 8- or 16-bit can be changed on the fly as often as you like.  My '816 Forth kernel leaves A at 16-bit and X & Y at 8-bit most of the time.

  3. You don't have to latch, decode, or use the high 8 bits of the 24-bit address bus.  You can ignore it and just use the low 16 bits (resulting in a 64K address space) if you wish.

  4. Although the '816 has 256 banks of 64KB each, it still has 256-byte pages, along with the DP (direct page) addressing modes that make for two-byte instructions.  DP is not called ZP, because it does not have to be in page zero.  It can be anywhere in the low 64K address space.  It doesn't even have to start on a page boundary.  You can move it around on the fly too.

  5. The bank boundaries are not transparent in most respects, so to answer part of schidester's question, although the program can be moved around within a bank, you wouldn't want to have it straddle a bank boundary if it wasn't written to do that.  The data bank and program bank registers allow you to operate in various banks without the inefficiency that would be presented if you always had to use 24-bit addresses; but you can still access any part of the 16MB address space by using long addressing modes (with 24-bit addresses) which ignore the bank registers.

_________________
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  
 Post subject:
PostPosted: Thu Feb 06, 2003 3:11 pm 
Offline

Joined: Wed Sep 04, 2002 4:08 pm
Posts: 57
Location: Iowa
Andre's format looks essentially like what a high-level language's linker would do; "compile" source code into binary object code but generate a table of addresses and symbols that need to be filled in at link time; in this case, the link always needs to be performed by the OS. I must admit I just skimmed the format and would like to go back and read it more in detail. It looks pretty nice, and for the project that started this discussion, is certainly something to consider.

Garth, thanks for the info on the 65816. Now I can't wait to get my hands on one!

Scott


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 25, 2023 6:51 pm 
Offline
User avatar

Joined: Wed May 11, 2022 10:34 am
Posts: 16
Location: Germany
Many say, relocatable code for the original 6502 without modifying the code at startup is difficult or not practical.
So I gave it a try, and here is what I came up with.
This support code becomes part of the initialization during program start (see attached example) and allow the user to call or jump to relative location within the program. In addition, a support routine is included, that returns addresses within the program code to manage data elements, like text.

The attached program example makes use of all three support routines and can be loaded to any place in RAM. It is written for the Ohio C1P computer
Attachment:
File comment: Example
RELOC_Example.zip [1.86 KiB]
Downloaded 127 times

Let me know, what you think about.

Code:
;
;   RELOCATION 6502 SUPPORT CODE (to support position independent asm programs)
;   This subroutines have to be loaded to fixed available RAM space
;   Concept: supporting JMP , JSR and ADR function at fixed free RAM area (Free_Mem)
;   Call JMP , target Address ((word)Target offset - * +1) -> Rel_JMP takes 85 cycles
;   Call JSR , target Address ((word)Target offset - * +1) -> Rel_JSR takes 110 cycle (without RTS)
;   Call ADR ; target Address ((word)Target offset - * +1) -> Rel_ADR takes 108 cycles and returns Address on stack (first high)
;   All three routines will not change ACCU, registers or status flags of the CPU !
;
;   Relocation support code needs 149 bytes of available RAM and is written for Ohio Superboard 600 / C1P
;   Zeropage address $FE and $FF are used for Adr_vector and must be available
;   Uses self-modifying code for register backup/restore



Free_Mem  = $026B

Rel_JMP   = Free_Mem            ; 149 Bytes free memory for Relocate code required
Rel_JSR   = Free_Mem + $2E
Rel_ADR   = Free_Mem + $60


temp_Al  = $FE               ; Free Zeropage (normally used for OSI Monitor Address)
temp_Ah  = $FF

      
   .ORG   Free_Mem
SUB_JMP;               ; ************ Jump Version <Rel_JMP>
   sta   temp_A+1         ; Save A
   sty   temp_Y+1         ; Save Y
    php
   pla
   sta   temp_S+1         ; Save Status
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of jump delta
   adc   temp_Al
   sta   jump_A+1
   iny
   lda   (temp_Al),y         ; Points to lowbyte of jump delta
   adc   temp_Ah
   sta   jump_A+2

temp_Y:   ldy   #0            ; Dummy values/address for restore
temp_S:   lda   #0   
   pha
temp_A:   lda   #0
   plp
jump_A:   jmp   0


SUB_JSR;               ; ************ JSR Version <Rel_JSR>
   sta   temp_A+1
   sty   temp_Y+1
    php
   pla
   sta   temp_S+1
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of jsr delta
   adc   temp_Al
   sta   jump_A+1
   iny
   lda   (temp_Al),y         ; Points to lowbyte of jsr delta
   adc   temp_Ah
   sta   jump_A+2
   clc               ; Correct return address
   lda   temp_Al
   adc   #2
   tay
   lda   temp_Ah
   adc   #0
   pha
   tya
   pha
   bcc   temp_Y            ; most of the time

   

SUB_ADR:               ; ************ Get Address onto stack <Rel_ADR>
   
   sta   tema_A+1
   sty   tema_Y+1
    php
   pla
   sta   tema_S+1
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of address delta
   adc   temp_Al
   pha               ; on stack
   iny
   lda   (temp_Al),y         ; Points to lowbyte of address delta
   adc   temp_Ah
   pha               ; on stack
   clc               ; Correct return address
   lda   temp_Al
   adc   #2
   tay
   lda   temp_Ah
   adc   #0
   pha
   tya
   pha
tema_Y:   ldy   #0            ; Dummy values for restore
tema_S:   lda   #0   
   pha
tema_A:   lda   #0
   plp
   rts


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 25, 2023 10:43 pm 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Nice idea. Different approaches have different pros and cons. I would suggest using TSX though to access the caller's address, to require less temporary storage. For JMP you can modify it in place. For JSR you can duplicate it and modify the copy. If you pass the offset in registers then you shouldn't need any temporary storage, something like this:

Code:
SUB_JMP:
    php
    pha
    txa
    pha
    tsx
    ; X is in A already now
    clc
    adc $104,x
    sta $104,x
    lda #0 ; or tya?
    adc $105,x
    sta $105,x
    pla
    tax
    pla
    plp
    rts

Apologies if I got the $104/$105 wrong, it's hard to remember!

I can also see a way to support the calling style I think you're using where the offset follows the JSR instruction in memory, still without requiring temporary storage in zero page... but it's tricky and complicated!


Last edited by gfoot on Sat Aug 26, 2023 9:20 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 25, 2023 11:14 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8438
Location: Southern California
Page 12 of my treatise on 6502 stacks (stacks plural, not just the page-1 hardware stack) is about "Where am I?" subroutines for relocatable code.  The following page is a peek at the 65816's new instructions and capabilities that are relevant to stacks, and 65c02 code which partially synthesizes some of them.

_________________
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: Sat Aug 26, 2023 10:54 am 
Offline
User avatar

Joined: Wed May 11, 2022 10:34 am
Posts: 16
Location: Germany
Thanks for the comments and feedback. To my understanding, this alternative code is taking Reg X and Y for the relative offset to the target address.
This is what I wanted to avoid, so a call or jump will not alter any register and the offset follows the JSR instruction in memory (like you expect from any call or jump)
As the 6502 only supports indirect indexed memory operation via zero page (aka LDA (Zero page),Y), that is the only way to retrieve the offset following the JSR instruction (all other are Absolute Indexed). If there is any other way not requiring temporary storage in zero page, that would be very interesting. Maybe self-modifying code.
I tried this method, but the code gets longer and takes longer to execute.
The chosen method also needs to clean up/modify the stack pointer, for of all three routines. This makes it difficult, if registers are put on stack and need to be retrieved at the end of the routine, and still having the return address corrected. I'm still convinced, that sacrificing a single pair of zero page temporary storage is worth the size and speed advantage. And the routines behave completely neutral concerning flags and register values, as they should.

The described "Where am I?" subroutines, I have seen before working on the code and it needs for relative subroutine call a pair of zero page bytes as an index register as well.
As it requires the offset in A and Y, I worked out another solution.
But still interested, if code size can be reduced.

The shown solution allows a programmer to easily convert existing programs to relocatable code and using status and other registers as usual in loops and other program flow situations.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 26, 2023 2:08 pm 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Yes I see, sorry I hadn't noticed that your "temp_A" was essentially self-modifying code. I think the only benefit to not using any storage is reentrancy, which is of questionable use anyway - I think it would only be necessary if interrupt service routines might call require relocatable code.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 26, 2023 7:13 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8438
Location: Southern California
The choices on the 65c02 and especially the 6502 (NMOS) will be pretty limited.  As it says at the beginning of the article, it's very inefficient, but not totally impossible.  And as shown there, you can improve the efficiency a little if you can accept relocation being only in the high byte of the address, leaving the low byte alone; for example, an application written to start at $0A41 could start at $1441, but not $0AB6.  In a sense, ZP is all processor registers; so don't be afraid to use them.  That's what they're there for.

_________________
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  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

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