General Information
Author: Bob Sander-Cederlof
Assembler: generic
Published: APPLE Assembly Line 01/82
Download: Apple Assembly Line Archive

Programs that are already assembled usually must be loaded at a specific memory address to execute properly. If you want to run it somewhere else, you have a problem. All the data references, JMP's, and JSR's will have to be examined to see if they need to be modified for the new location. If you don't have the source code, you can't re-assemble it. The other way, patching, can be quite a tedious operation!

Fortunately, way back in 1977, the WOZ (Steve Wozniak to you newcomers) wrote a program to do the work automatically. If you have the Programmer's Aid ROM then you have his RELOCATE program. You who have Apple II Plusses with the Language Card (also called 16K RAM card) can also use his program, because it is in the INTBASIC file along with Integer BASIC. (The latter group of people probably don't have the manual, though, because they didn't buy the ROM.)

I would like to see the RELOCATE program made more widely available, but it cannot be used as is unless you have Integer BASIC. Why? Because it uses SWEET-16 opcodes. RELOCATE also is itself tied to running at whatever location it is assembled for, so it can be a little trouble to find a place for it sometimes. By now you have probably guessed that I have recoded RELOCATE to solve both of these problems!

Paul Schlyter's article elsewhere in this issue of AAL shows RELOCATE put to good use. You can examine his instructions and learn most of what you need to know to use RELOCATE on your own programs. Basically, there are four steps:

1. Initialize. This sets up the control-Y monitor command. If RELOCATE is on a file, you do this with "BRUN RELOCATE".

2. Specify the program start and end addresses (where it now is in memory), and the new starting address (where you want it to be relocated to). This is done with the monitor command: {code}target<start.end^Y*{code} where "target" is the new starting address, and "start" and "end" are the addresses of the program where it is now. "^Y" means "control-Y". The "\*" after the control-Y signals RELOCATE that you are in step 2 rather than step 3 or 4.

3. Specify the FIRST block to be copied "as-is" or to be "relocated" to the destination area. This is done with the monitor command: {code}target<start.end^Y{code} or {code}target<start.endM{code} where "target" is the starting address in the new area for this block, and "start" and "end" define the block itself. Note that there is no trailing asterisk this time. Use control-Y if you want this block relocated, or M if you want it copied as-is.

4. Specify the NEXT block to be copied as-is or relocated. You do this with the monitor command: {code}.end^Y{code} or {code}.endM{code} where the target and start addresses are assumed to immediately follow the previously handled block, and "end" specifies the end of this new block. Use control-Y to relocate the block, or M to copy it as-is.

Obviously, step 4 above is repeated until the whole program has been copied/relocated. For each block of your program that is to be copied as-is, with no modification at all, you use the "M" command; for each block to be relocated you use the "control-Y" command.

If you need more detailed instructions and explanation, I must refer you to the manual. The Programmer's Aid #1 Manual is sold at most computer stores separately from the ROM package. Pages 11-28 explain why and how to use RELOCATE, and pages 80 and 81 contain the assembly listing.

Now here is my new version, which can be BRUN anywhere you have 134 ($86) bytes available. I have eliminated the SWEET-16 usage; this made the program slightly bigger, and a lot faster.

Lines 1260-1380 are the initialization code. They build the control-Y vector at $3F8-3FA. A JMP opcode is stored at $3F8; if you have DOS up this is redundant, but it won't hurt. Next I have to try to find myself. That is, where in memory am I (the program RELOCATE) located? JSR MON.RETURN (which is only an RTS instruction, so it comes right back without doing anything) puts the address of the third byte of the JSR instruction on the stack. Lines 1290-1370 use that address to compute the address of RELOC, and store it in $3F9 and $3FA.

When you type in a control-Y command, the monitor will now branch to RELOC at line 1400. Lines 1400-1430 look at the character after the control-Y in the command input buffer; if it is an asterisk, then you are trying to do step 2 above. If not, then you are on step 3 or 4. Lines 1440-1500 handle step 2, and lines 1510-1990 handle steps 3 and 4.

The part which used to be coded in SWEET-16 was lines 1690-1880. The SWEET-16 version took only 14 bytes, while the 6502 code takes 34 bytes. The 6502 version may take about 100 microseconds to execute, and the SWEET-16 version on the order of 1000 microseconds (for each instruction relocated).


 1000  *
 1010  *		6502 RELOCATION SUBROUTINE
 1020  *
 1030  *		MAY BE LOADED ANYWHERE, AS IT IS SELF-RELOCATABLE
 1040  *
 1050  *		ADAPTED FROM SIMILAR PROGRAM IN PROGRAMMERS AID #1
 1060  *		ORIGINAL PROGRAM BY WOZ, 11-10-77
 1070  *		ADAPTED BY BOB SANDER-CEDERLOF, 12-30-81
 1080  *		(ELIMINATED USAGE OF SWEET-16)
 1090  *
 1100  MON.YSAV	.EQ $34  COMMAND BUFFER POINTER
 1110  MON.LENGTH .EQ $2F  # BYTES IN INSTRUCTION - 1
 1120  MON.INSDS2 .EQ $F88E  DISASSEMBLE (FIND LENGTH OF OPCODE)
 1130  MON.NXTA4  .EQ $FCB4	  UPDATE POINTERS, TEST FOR END
 1140  MON.RETURN .EQ $FF58
 1150  STACK		.EQ $0100	  SYSTEM STACK
 1160  INBUF		.EQ $0200	  COMMAND INPUT BUFFER
 1170  *
 1180  A1	  .EQ $3C,3D
 1190  A2	  .EQ $3E,3F
 1200  A4	  .EQ $42,43
 1210  R1	  .EQ $02,03
 1220  R2	  .EQ $04,05
 1230  R4	  .EQ $08,09
 1240  INST	.EQ $0A,0B,0C
 1250  *
 1260  START  LDA #$4C	  JMP OPCODE
 1270			STA $3F8	  BUILD CONTROL-Y VECTOR
 1280			JSR MON.RETURN	 FIND OUT WHERE I AM FIRST
 1290  START1 TSX
 1300			DEX			 POINT AT LOW BYTE
 1310			SEC			 +1
 1320			LDA STACK,X  LOW BYTE OF START1-1
 1330			ADC #RELOC-START1
 1340			STA $3F9
 1350			LDA STACK+1,X	HIGH BYTE OF START1-1
 1360			ADC /RELOC-START1
 1370			STA $3FA
 1380			RTS
 1390  *
 1400  RELOC  LDY MON.YSAV COMMAND BUFFER POINTER
 1410			LDA INBUF,Y  GET CHAR AFTER CONTROL-Y
 1420			CMP #$AA	  IS IT "*"?
 1430			BNE RELOC2	NO, RELOCATE A BLOCK
 1440			INC MON.YSAV YES, GET BLOCK DEFINITION
 1450			LDX #7		 COPY A1, A2, AND A4
 1460  .1	  LDA A1,X
 1470			STA R1,X
 1480			DEX
 1490			BPL .1
 1500			RTS
 1510  *
 1520  RELOC2 LDY #2		 COPY NEXT 3 BYTES FOR MY USE
 1530  .1	  LDA (A1),Y
 1540			STA INST,Y
 1550			DEY
 1560			BPL .1
 1570			JSR MON.INSDS2  GET LENGTH OF INSTRUCTION
 1580			LDX MON.LENGTH  0=1 BYTE, 1=2 BYTES, 2=3 BYTES
 1590			BEQ .3		 1-BYTE OPCODE
 1600			DEX
 1610			BNE .2		 3-BYTE OPCODE
 1620			LDA INST	  2-BYTE OPCODE
 1630			AND #$0D	  SEE IF ZERO-PAGE MODE
 1640			BEQ .3		 NO (X0 OR X2 OPCODE)
 1650			AND #$08
 1660			BNE .3		 NO (80-FF OPCODE)
 1670			STA INST+2	CLEAR HIGH BYTE OF ADDRESS FIELD
 1680  *
 1690  .2	  LDA R2		 COMPARE ADDR TO END OF SOURCE BLOCK
 1700			CMP INST+1
 1710			LDA R2+1
 1720			SBC INST+2
 1730			BCC .3		 ADDR > SRCEND
 1740			SEC			 COMPARE ADDR TO BEGINNING OF SRC
 1750			LDA INST+1
 1760			SBC R1
 1770			TAY
 1780			LDA INST+2
 1790			SBC R1+1
 1800			BCC .3		 ADDR < SRCBEG
 1810			TAX
 1820			TYA			 ADDR = ADDR-SRCBEG+DESTBEG
 1830			CLC
 1840			ADC R4
 1850			STA INST+1
 1860			TXA
 1870			ADC R4+1
 1880			STA INST+2
 1890  *
 1900  .3	  LDX #0		 COPY MODIFIED INSTRUCTION TO DESTINATION
 1910			LDY #0
 1920  .4	  LDA INST,X	NEXT BYTE OF THIS INSTRUCTION
 1930			STA (A4),Y
 1940			INX
 1950			JSR MON.NXTA4	 ADVANCE A1 AND A4, TEST FOR END
 1960			DEC MON.LENGTH  TEST FOR END OF THIS INSTRUCTION
 1970			BPL .4		 MORE IN THIS INSTRUCTION
 1980			BCC RELOC2	END OF SOURCE BLOCK
 1990			RTS


Comment:

Bob Sander-Cederlof | 19.11.2007 at 03:59 PM Thank you for republishing my article. The Apple Assembly Line newsletter was published from monthly October 1980 through May 1988. All the issues are available online at http://www.txbobsc.com/aal/