1 /*
2 * This file is part of libsidplayfp, a SID player engine.
3 *
4 * Copyright (C) 2013-2016 Leandro Nini
5 * Copyright (C) 2001 Dag Lem
6 * Copyright (C) 1989-1997 Andr� Fachat (a.fachat@physik.tu-chemnitz.de)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "reloc65.h"
24
25 #include <cstring>
26
27 #include "sidplayfp/siddefs.h"
28
29 /// 16 bit header
30 const int HEADER_SIZE = (8 + 9 * 2);
31
32 /// Magic number
33 const unsigned char o65hdr[] = {1, 0, 'o', '6', '5'};
34
35 /**
36 * Read a 16 bit word from a buffer at specific location.
37 *
38 * @param buffer
39 * @param idx
40 */
getWord(unsigned char * buffer,int idx)41 inline int getWord(unsigned char *buffer, int idx)
42 {
43 return buffer[idx] | (buffer[idx+1] << 8);
44 }
45
46 /**
47 * Write a 16 bit word into a buffer at specific location.
48 *
49 * @param buffer
50 * @param idx
51 * @param value
52 */
setWord(unsigned char * buffer,int idx,int value)53 inline void setWord(unsigned char *buffer, int idx, int value)
54 {
55 buffer[idx] = value & 0xff;
56 buffer[idx+1] = (value >> 8) & 0xff;
57 }
58
59 /**
60 * Get the size of header options section.
61 *
62 * @param buf
63 */
read_options(unsigned char * buf)64 inline int read_options(unsigned char *buf)
65 {
66 int l = 0;
67
68 unsigned char c = buf[0];
69 while (c)
70 {
71 l += c;
72 c = buf[l];
73 }
74 return ++l;
75 }
76
77 /**
78 * Get the size of undefined references list.
79 *
80 * @param buf
81 */
read_undef(unsigned char * buf)82 inline int read_undef(unsigned char *buf)
83 {
84 int l = 2;
85
86 int n = getWord(buf, 0);
87 while (n)
88 {
89 n--;
90 while (!buf[l++]) {}
91 }
92 return l;
93 }
94
reloc65(int addr)95 reloc65::reloc65(int addr) :
96 m_tbase(addr) {}
97
reloc(unsigned char ** buf,int * fsize)98 bool reloc65::reloc(unsigned char **buf, int *fsize)
99 {
100 unsigned char *tmpBuf = *buf;
101
102 if (memcmp(tmpBuf, o65hdr, 5) != 0)
103 {
104 return false;
105 }
106
107 const int mode = getWord(tmpBuf, 6);
108 if ((mode & 0x2000) // 32 bit size not supported
109 || (mode & 0x4000)) // pagewise relocation not supported
110 {
111 return false;
112 }
113
114 const int hlen = HEADER_SIZE + read_options(tmpBuf + HEADER_SIZE);
115
116 const int tbase = getWord(tmpBuf, 8);
117 const int tlen = getWord(tmpBuf, 10);
118 m_tdiff = m_tbase - tbase;
119
120 const int dlen = getWord(tmpBuf, 14);
121
122 unsigned char *segt = tmpBuf + hlen; // Text segment
123 unsigned char *segd = segt + tlen; // Data segment
124 unsigned char *utab = segd + dlen; // Undefined references list
125
126 unsigned char *rttab = utab + read_undef(utab); // Text relocation table
127
128 unsigned char *rdtab = reloc_seg(segt, tlen, rttab); // Data relocation table
129 unsigned char *extab = reloc_seg(segd, dlen, rdtab); // Exported globals list
130
131 reloc_globals(extab);
132
133 setWord(tmpBuf, 8, m_tbase);
134
135 *buf = segt;
136 *fsize = tlen;
137 return true;
138 }
139
reldiff(unsigned char s)140 int reloc65::reldiff(unsigned char s)
141 {
142 return s==2 ? m_tdiff : 0;
143 }
144
reloc_seg(unsigned char * buf,int len,unsigned char * rtab)145 unsigned char* reloc65::reloc_seg(unsigned char *buf, int len, unsigned char *rtab)
146 {
147 int adr = -1;
148 while (*rtab)
149 {
150 if ((*rtab & 255) == 255)
151 {
152 adr += 254;
153 rtab++;
154 }
155 else
156 {
157 adr += *rtab & 255;
158 rtab++;
159 const unsigned char type = *rtab & 0xe0;
160 unsigned char seg = *rtab & 0x07;
161 rtab++;
162 switch(type)
163 {
164 case 0x80: {
165 const int oldVal = getWord(buf, adr);
166 const int newVal = oldVal + reldiff(seg);
167 setWord(buf, adr, newVal);
168 break; }
169 case 0x40: {
170 const int oldVal = buf[adr] * 256 + *rtab;
171 const int newVal = oldVal + reldiff(seg);
172 buf[adr] = (newVal >> 8) & 255;
173 *rtab = newVal & 255;
174 rtab++;
175 break; }
176 case 0x20: {
177 const int oldVal = buf[adr];
178 const int newVal = oldVal + reldiff(seg);
179 buf[adr] = newVal & 255;
180 break; }
181 }
182 if (seg == 0)
183 {
184 rtab += 2;
185 }
186 }
187
188 if (adr > len)
189 {
190 // Warning: relocation table entries past segment end!
191 }
192 }
193
194 return ++rtab;
195 }
196
reloc_globals(unsigned char * buf)197 unsigned char *reloc65::reloc_globals(unsigned char *buf)
198 {
199 int n = getWord(buf, 0);
200 buf +=2;
201
202 while (n)
203 {
204 while (*(buf++)) {}
205 unsigned char seg = *buf;
206 const int oldVal = getWord(buf, 1);
207 const int newVal = oldVal + reldiff(seg);
208 setWord(buf, 1, newVal);
209 buf +=3;
210 n--;
211 }
212
213 return buf;
214 }
215