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