1 /*
2     xa65 - 6502 cross assembler and utility suite
3     reloc65 - relocates 'o65' files
4     Copyright (C) 1997 Andr� Fachat (a.fachat@physik.tu-chemnitz.de)
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 /*
22     Modified by Dag Lem <resid@nimrod.no>
23     Relocate and extract text segment from memory buffer instead of file.
24     For use with VICE VSID.
25 */
26 
27 #include <stdio.h>
28 #include <string.h>
29 
30 #define	BUF	(9*2+8)		/* 16 bit header */
31 
32 typedef struct {
33 	char 		*fname;
34 	size_t 		fsize;
35 	unsigned char	*buf;
36 	int		tbase, tlen, dbase, dlen, bbase, blen, zbase, zlen;
37 	int		tdiff, ddiff, bdiff, zdiff;
38 	unsigned char	*segt;
39 	unsigned char	*segd;
40 	unsigned char 	*utab;
41 	unsigned char 	*rttab;
42 	unsigned char 	*rdtab;
43 	unsigned char 	*extab;
44 } file65;
45 
46 
47 int read_options(unsigned char *f);
48 int read_undef(unsigned char *f);
49 unsigned char *reloc_seg(unsigned char *f, int len, unsigned char *rtab, file65 *fp);
50 unsigned char *reloc_globals(unsigned char *, file65 *fp);
51 
52 file65 file;
53 unsigned char cmp[] = { 1, 0, 'o', '6', '5' };
54 
reloc65(unsigned char ** buf,int * fsize,int addr)55 int reloc65(unsigned char** buf, int* fsize, int addr)
56 {
57 	int mode, hlen;
58 
59 	int tflag=0, dflag=0, bflag=0, zflag=0;
60 	int tbase=0, dbase=0, bbase=0, zbase=0;
61 	int extract = 0;
62 
63 	file.buf = *buf;
64 	file.fsize = *fsize;
65 	tflag= 1;
66 	tbase = addr;
67 	extract = 1;
68 
69 	if (memcmp(file.buf, cmp, 5) != 0) {
70 		return 0;
71 	}
72 
73 	mode=file.buf[7]*256+file.buf[6];
74 	if(mode & 0x2000) {
75 		return 0;
76 	} else if(mode & 0x4000) {
77 		return 0;
78 	}
79 
80 	hlen = BUF+read_options(file.buf+BUF);
81 
82 	file.tbase = file.buf[ 9]*256+file.buf[ 8];
83 	file.tlen  = file.buf[11]*256+file.buf[10];
84 	file.tdiff = tflag? tbase - file.tbase : 0;
85 	file.dbase = file.buf[13]*256+file.buf[12];
86 	file.dlen  = file.buf[15]*256+file.buf[14];
87 	file.ddiff = dflag? dbase - file.dbase : 0;
88 	file.bbase = file.buf[17]*256+file.buf[16];
89 	file.blen  = file.buf[19]*256+file.buf[18];
90 	file.bdiff = bflag? bbase - file.bbase : 0;
91 	file.zbase = file.buf[21]*256+file.buf[20];
92 	file.zlen  = file.buf[23]*256+file.buf[21];
93 	file.zdiff = zflag? zbase - file.zbase : 0;
94 
95 	file.segt  = file.buf + hlen;
96 	file.segd  = file.segt + file.tlen;
97 	file.utab  = file.segd + file.dlen;
98 
99 	file.rttab = file.utab + read_undef(file.utab);
100 
101 	file.rdtab = reloc_seg(file.segt, file.tlen, file.rttab, &file);
102 	file.extab = reloc_seg(file.segd, file.dlen, file.rdtab, &file);
103 
104 	reloc_globals(file.extab, &file);
105 
106 	if(tflag) {
107 		file.buf[ 9]= (tbase>>8)&255;
108 		file.buf[ 8]= tbase & 255;
109 	}
110   	if(dflag) {
111 		file.buf[13]= (dbase>>8)&255;
112 		file.buf[12]= dbase & 255;
113 	}
114 	if(bflag) {
115 		file.buf[17]= (bbase>>8)&255;
116 		file.buf[16]= bbase & 255;
117 	}
118 	if(zflag) {
119 		file.buf[21]= (zbase>>8)&255;
120 		file.buf[20]= zbase & 255;
121 	}
122 
123 	switch(extract) {
124 	case 0:	/* whole file */
125 		return 1;
126 	case 1:	/* text segment */
127 		*buf = file.segt;
128 		*fsize = file.tlen;
129 		return 1;
130 	case 2:
131 		*buf = file.segd;
132 		*fsize = file.dlen;
133 		return 1;
134 	default:
135 		return 0;
136 	}
137 }
138 
139 
read_options(unsigned char * buf)140 int read_options(unsigned char *buf) {
141 	int c, l=0;
142 
143 	c=buf[0];
144 	while(c && c!=EOF) {
145 	  c&=255;
146 	  l+=c;
147 	  c=buf[l];
148 	}
149 	return ++l;
150 }
151 
read_undef(unsigned char * buf)152 int read_undef(unsigned char *buf) {
153 	int n, l = 2;
154 
155 	n = buf[0] + 256*buf[1];
156 	while(n){
157 	  n--;
158 	  while(!buf[l++]);
159 	}
160 	return l;
161 }
162 
163 #define	reldiff(s)	(((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0))))
164 
reloc_seg(unsigned char * buf,int len,unsigned char * rtab,file65 * fp)165 unsigned char *reloc_seg(unsigned char *buf, int len, unsigned char *rtab, file65 *fp) {
166 	int adr = -1;
167 	int type, seg, old, new;
168 /*printf("tdiff=%04x, ddiff=%04x, bdiff=%04x, zdiff=%04x\n",
169 		fp->tdiff, fp->ddiff, fp->bdiff, fp->zdiff);*/
170 	while(*rtab) {
171 	  if((*rtab & 255) == 255) {
172 	    adr += 254;
173 	    rtab++;
174 	  } else {
175 	    adr += *rtab & 255;
176 	    rtab++;
177 	    type = *rtab & 0xe0;
178 	    seg = *rtab & 0x07;
179 /*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",rtab-1, *(rtab-1), adr, type, seg);*/
180 	    rtab++;
181 	    switch(type) {
182 	    case 0x80:
183 		old = buf[adr] + 256*buf[adr+1];
184 		new = old + reldiff(seg);
185 		buf[adr] = new & 255;
186 		buf[adr+1] = (new>>8)&255;
187 		break;
188 	    case 0x40:
189 		old = buf[adr]*256 + *rtab;
190 		new = old + reldiff(seg);
191 		buf[adr] = (new>>8)&255;
192 		*rtab = new & 255;
193 		rtab++;
194 		break;
195 	    case 0x20:
196 		old = buf[adr];
197 		new = old + reldiff(seg);
198 		buf[adr] = new & 255;
199 		break;
200 	    }
201 	    if(seg==0) rtab+=2;
202 	  }
203 	}
204 	if(adr > len) {
205 /*
206 	  fprintf(stderr,"reloc65: %s: Warning: relocation table entries past segment end!\n",
207 		fp->fname);
208 */
209 	}
210 	return ++rtab;
211 }
212 
reloc_globals(unsigned char * buf,file65 * fp)213 unsigned char *reloc_globals(unsigned char *buf, file65 *fp) {
214 	int n, old, new, seg;
215 
216 	n = buf[0] + 256*buf[1];
217 	buf +=2;
218 
219 	while(n) {
220 /*printf("relocating %s, ", buf);*/
221 	  while(*(buf++));
222 	  seg = *buf;
223 	  old = buf[1] + 256*buf[2];
224 	  new = old + reldiff(seg);
225 /*printf("old=%04x, seg=%d, rel=%04x, new=%04x\n", old, seg, reldiff(seg), new);*/
226 	  buf[1] = new & 255;
227 	  buf[2] = (new>>8) & 255;
228 	  buf +=3;
229 	  n--;
230 	}
231 	return buf;
232 }
233