1 package com.tapina.robe.runtime;
2
3 import com.tapina.robe.module.Module;
4 import com.tapina.robe.module.ModuleUtils;
5 import com.tapina.robe.module.RelocatableModuleArea;
6 import com.tapina.robe.swi.*;
7 import com.tapina.robe.swi.Font;
8
9 import java.io.UnsupportedEncodingException;
10 import java.util.*;
11 import java.util.logging.Logger;
12 import java.awt.*;
13
14 /***
15 * This class represents the memory map of a running application.
16 * Some of the memory map is shared between applications and is therefore represented as static fields.
17 */
18 public final class MemoryMap extends BinaryDataSource {
19 static final Logger log = Logger.getLogger(MemoryMap.class.getName());
20 private static final int SYSTEM_MEMORY_BASE = 0x1800000;
21 private static final int ROM_BASE = 0x3800000;
22 private static final int ROM_TOP = 0x47fffff;
23 private static final int DYNAMIC_AREA_MEMORY_BASE = 0x50000000;
24 private static final int DEFAULT_DYNAMIC_AREA_SIZE = 4 * 1024 * 1024;
25 final static Module[] romModules = new Module[]{
26 ColourPicker.getInstance(), ColourTrans.getInstance(),
27 DDEUtils.getInstance(), Font.getInstance(),
28 FPEmulator.getInstance(), Hourglass.getInstance(), MessageTrans.getInstance(),
29 OS.getInstance(), OSExt.getInstance(), OSExt3.getInstance(), SharedCLibrary.getInstance(),
30 SharedUnixLibrary.getInstance(), SpriteExtend.getInstance(), TaskManager.getInstance(), TaskWindow.getInstance(),
31 Territory.getInstance(), Toolbox.getInstance(), VectorUtil.getInstance(), Wimp.getInstance()
32 };
33
34 static {
35 initialiseROMModules();
36 }
37
38 private final int loadAddress;
39 private final byte[] image;
40 private final RawDataBlock imageData;
41 private final SortedMap codeMap;
42 final SortedMap dataMap;
43 static final LinkedList dynamicAreas = new LinkedList();
44 static final RelocatableModuleArea rma = new RelocatableModuleArea();
45
46 public MemoryMap(int loadAddress, byte[] image) {
47 this.loadAddress = loadAddress;
48 this.image = image;
49 codeMap = new TreeMap();
50 dataMap = new TreeMap();
51
52 imageData = new RawDataBlock(loadAddress, image);
53 dataMap.put(new Integer(loadAddress), imageData);
54 }
55
56 private static void initialiseROMModules() {
57 for (int i = 0, address = ROM_BASE; i < romModules.length; i++) {
58 final Module romModule = romModules[i];
59 romModule.setAddress(address);
60 ModuleUtils.registerModule(romModule);
61 address += romModule.getSize();
62 }
63 }
64
65 public final int getImageSize() {
66 return imageData.getSize();
67 }
68
69 public final void setImageSize(int size) {
70 if (size < imageData.getSize()) {
71 log.warning("Attempting to reduce size of image to &" + Integer.toHexString(size) + " from " + Integer.toHexString(imageData.getSize()));
72 return;
73 } else if (dataMap.size() > 1) {
74 log.info("Attempting to change image size when other data blocks have already been allocated - ignoring");
75 return;
76 } else {
77 imageData.extendForwards(size - imageData.getSize());
78 }
79
80 }
81
82 public final int getLoadAddress() {
83 return loadAddress;
84 }
85
86 public final CodeBlock addEntryPoint(int address) {
87 Integer addressInt = new Integer(address);
88 if (!codeMap.isEmpty() && codeMap.containsKey(addressInt)) {
89 return (CodeBlock) codeMap.get(addressInt);
90 } else {
91
92 CodeBlock codeBlock = new InterpretedCodeBlock(address);
93 codeMap.put(addressInt, codeBlock);
94 return codeBlock;
95 }
96 }
97
98 public final CodeBlock addEntryPoint(CodeBlock codeBlock) {
99 codeMap.put(new Integer(codeBlock.getAddress()), codeBlock);
100 return codeBlock;
101 }
102
103 public final Instruction fetchAndDecode(int address) throws DecoderException {
104 try {
105 final int instructionWord = fetchInstruction(address);
106 final Instruction instruction = Decoder.decode(instructionWord);
107 return instruction;
108 } catch (DecoderException e) {
109 e.setAddress(address);
110 throw e;
111 } catch (ArrayIndexOutOfBoundsException e) {
112 log.severe("Attempt to execute outside code boundaries (&" + Integer.toHexString(address) + ")");
113 throw e;
114 }
115 }
116
117 final int fetchInstruction(int address) {
118 final int instructionWord;
119 if (address - loadAddress < imageData.getSize()) {
120
121 instructionWord = ByteArrayUtils.getInt(imageData.getBytes(), address - loadAddress);
122 } else {
123 instructionWord = getWord(address);
124 if (instructionWord == 0) {
125 log.severe("Fetched dubious instruction from &" + Integer.toHexString(address));
126 }
127 }
128 return instructionWord;
129 }
130
131 public final DataBlock getDataBlock(int address) {
132 return (DataBlock) dataMap.get(new Integer(address));
133 }
134
135 DataBlock findDataBlock(int address, int length) {
136 final BlockLocator locator = new BlockLocator(this, address, length);
137 return locator.findDataBlock();
138 }
139
140 static boolean isDynamicAreaAddress(int address) {
141 return address >= DYNAMIC_AREA_MEMORY_BASE;
142 }
143
144 static boolean isRomAddress(int address) {
145 return address >= ROM_BASE && address <= ROM_TOP;
146 }
147
148 public final RawDataBlock createDataBlock(int address, int length) {
149 final RawDataBlock dataBlock = new RawDataBlock(address, length);
150 dataMap.put(new Integer(address), dataBlock);
151 return dataBlock;
152 }
153
154 /***
155 * Create a system data block large enough to hold a null-terminated string and fill it with the string.
156 *
157 * @param str string to populate block with
158 * @return populated data block
159 */
160 public final RawDataBlock createSystemDataBlock(String str) {
161 final RawDataBlock block = createSystemDataBlock(str.length() + 1);
162 if (block != null) {
163 ByteArrayUtils.putString0(block.getBytes(), 0, str);
164 }
165 return block;
166 }
167
168 public final RawDataBlock createSystemDataBlock(int length) {
169 final int address;
170 if (!dataMap.containsKey(new Integer(SYSTEM_MEMORY_BASE))) {
171 address = SYSTEM_MEMORY_BASE;
172 } else {
173 final DataBlock lastBlock = (DataBlock) dataMap.get(dataMap.lastKey());
174 address = (lastBlock.getAddress() + lastBlock.getSize() + 3) & ~3;
175 }
176 return createDataBlock(address, length);
177 }
178
179 public void resizeSystemDataBlock(DataBlock oldBlock, int newSize) {
180
181 final DataBlock lastBlock = (DataBlock) dataMap.get(dataMap.lastKey());
182 if (oldBlock.getAddress() != lastBlock.getAddress() && newSize > oldBlock.getSize()) {
183 dataMap.remove(new Integer(oldBlock.getAddress()));
184 oldBlock.setAddress((lastBlock.getAddress() + lastBlock.getSize() + 3) & ~3);
185 dataMap.put(new Integer(oldBlock.getAddress()), oldBlock);
186 }
187 oldBlock.setSize(newSize);
188 }
189
190 public final DataBlock claimRmaBlock(int length) {
191 return rma.claimBlock(length);
192 }
193
194 public final void releaseRmaBlock(int address) {
195 rma.freeBlock(address);
196 }
197
198 /***
199 * Create a new dynamic area. Area numbers allocated are greater than or equal to 256.
200 *
201 * @param initialSize initial size of area in bytes
202 * @param maximumSize maximum size of area in bytes (-1 for unlimited [deprecated])
203 * @param handlerRoutine address of handler routine
204 * @param handlerWorkspace pointer to workspace for handler (-1 for base of area)
205 * @param name name of area
206 * @param privileges access privileges to be given to area
207 * @param flags settings for area
208 * @return newly created dynamic area
209 */
210 public final DynamicArea createDynamicArea(int initialSize, int maximumSize, int handlerRoutine, int handlerWorkspace,
211 String name, int privileges, int flags) {
212 if (maximumSize == -1) {
213 log.warning("Unlimited maximum dynamic area size not supported, defaulting to 4M");
214 maximumSize = DEFAULT_DYNAMIC_AREA_SIZE;
215 }
216 final int base;
217 if (dynamicAreas.size() > 0) {
218 final DynamicArea topArea = ((DynamicArea) dynamicAreas.getLast());
219 base = (topArea.getBase() + topArea.getMaximumSize() + 3) & (~3);
220 } else {
221 base = DYNAMIC_AREA_MEMORY_BASE;
222 }
223 final DynamicArea newArea = new DynamicArea(base, maximumSize, initialSize, handlerRoutine, handlerWorkspace, name, privileges, flags);
224 synchronized (dynamicAreas) {
225 dynamicAreas.add(newArea);
226 }
227 return newArea;
228 }
229
230 /***
231 * Find a previously-created dyanmic area, given its number.
232 *
233 * @param areaNumber dynamic area unique number
234 * @return dynamic area representation, or null if not found
235 */
236 public DynamicArea findDynamicArea(int areaNumber) {
237 for (Iterator iterator = dynamicAreas.iterator(); iterator.hasNext();) {
238 DynamicArea dynamicArea = (DynamicArea) iterator.next();
239 if (dynamicArea.getAreaNumber() == areaNumber) {
240 return dynamicArea;
241 }
242 }
243 return null;
244 }
245
246 public final void removeSystemDataBlock(DataBlock block) {
247 dataMap.remove(new Integer(block.getAddress()));
248 }
249
250 public final void replaceDataBlock(DataBlock oldDataBlock, DataBlock newDataBlock) {
251 newDataBlock.setAddress(oldDataBlock.getAddress());
252 dataMap.put(new Integer(newDataBlock.getAddress()), newDataBlock);
253 }
254
255
256 String getTerminatedString(int address, StringTerminator terminator, int maxLength) {
257 StringBuffer buffer = new StringBuffer(255);
258
259 try {
260 do {
261 DataBlock block = findDataBlock(address, 1);
262 if (block == null) {
263 throw new AddressException(address);
264 }
265 byte[] bytes = block.getBytes();
266
267 int endOffset;
268 final int startOffset = address - block.getAddress();
269 for (endOffset = startOffset; endOffset < bytes.length; endOffset++) {
270 if ((endOffset - startOffset) >= maxLength) break;
271 if (terminator.isTerminalByte(bytes[endOffset])) break;
272 }
273 buffer.append(new String(bytes, startOffset, endOffset - startOffset, "ISO-8859-1"));
274 if (endOffset < bytes.length) {
275
276 return buffer.toString();
277 }
278
279 address = block.getAddress() + bytes.length;
280 } while (true);
281 } catch (UnsupportedEncodingException e) {
282
283 throw new RuntimeException(e);
284 }
285 }
286
287
288 public final byte getByte(int address) {
289 DataBlock block = findDataBlock(address, 1);
290 if (block == null) {
291 throw new AddressException(address);
292 }
293 return block.getBytes()[address - block.getAddress()];
294 }
295
296 public final ByteArray getByteArray(int address, int count, boolean create) {
297 DataBlock block = findDataBlock(address, count);
298 if (block == null) {
299 if (create) {
300 block = createDataBlock(address, count);
301 } else {
302 throw new AddressException(address);
303 }
304 }
305 return new ByteArray(block.getBytes(), address - block.getAddress(), count);
306 }
307
308 public final byte[] getBytes(int address, int count) {
309 DataBlock block = findDataBlock(address, count);
310 if (block == null) {
311 throw new AddressException(address);
312 }
313 byte[] data = new byte[count];
314 System.arraycopy(block.getBytes(), address - block.getAddress(), data, 0, count);
315 return data;
316 }
317
318
319
320
321
322 public final int getWord(int address) {
323 DataBlock block;
324 try {
325 block = findDataBlock(address, 4);
326 } catch (OutOfMemoryError e) {
327 throw new AddressException(address);
328 }
329 if (block == null) {
330 throw new AddressException(address);
331 }
332 return ByteArrayUtils.getInt(block.getBytes(), address - block.getAddress());
333 }
334
335 public final long get5ByteValue(int address) {
336 DataBlock block;
337 try {
338 block = findDataBlock(address, 5);
339 } catch (OutOfMemoryError e) {
340 throw new AddressException(address);
341 }
342 if (block == null) {
343 throw new AddressException(address);
344 }
345 return ByteArrayUtils.get5ByteValue(block.getBytes(), address - block.getAddress());
346 }
347
348 public final int[] getWords(int address, int count) {
349 DataBlock block = findDataBlock(address, count * 4);
350 if (block == null) {
351 throw new AddressException(address);
352 }
353 int[] words = new int[count];
354 for (count--; count >= 0; count--) {
355 words[count] = ByteArrayUtils.getInt(block.getBytes(), address - block.getAddress() + (count * 4));
356 }
357 return words;
358 }
359
360 public final void storeByte(int address, byte aByte) {
361 DataBlock block = findDataBlock(address, 1);
362 if (block == null) {
363 block = createDataBlock(address, 4);
364 }
365 block.getBytes()[address - block.getAddress()] = aByte;
366 }
367
368 public final void storeBytes(int address, byte[] bytes, int offset, int length) {
369 DataBlock block = findDataBlock(address, length);
370 if (block == null) {
371 block = createDataBlock(address, length);
372 }
373 System.arraycopy(bytes, offset, block.getBytes(), address - block.getAddress(), length);
374 }
375
376
377 public final void storeWord(int address, int word) {
378 DataBlock block = findDataBlock(address, 4);
379 if (block == null) {
380 block = createDataBlock(address, 4);
381 }
382 ByteArrayUtils.putInt(block.getBytes(), address - block.getAddress(), word);
383 }
384
385 public final void store5ByteValue(int address, long value) {
386 DataBlock block = findDataBlock(address, 5);
387 if (block == null) {
388 block = createDataBlock(address, 5);
389 }
390 ByteArrayUtils.put5ByteValue(block.getBytes(), address - block.getAddress(), value);
391 }
392
393 public final void storeWords(int address, int[] words) {
394 final int length = words.length * 4;
395 DataBlock block = findDataBlock(address, length);
396 if (block == null) {
397 block = createDataBlock(address, length);
398 }
399 for (int count = 0; count < words.length; count++) {
400 ByteArrayUtils.putInt(block.getBytes(), address - block.getAddress() + (count * 4), words[count]);
401 }
402 }
403
404 private DataBlock currentErrorBlock = null;
405
406 public final RawDataBlock createErrorBlock(int size) {
407 if (currentErrorBlock != null) {
408 dataMap.remove(new Integer(currentErrorBlock.getAddress()));
409 }
410 RawDataBlock dataBlock;
411 if (dataMap.isEmpty()) {
412 dataBlock = new RawDataBlock(loadAddress + image.length, size);
413 } else {
414 DataBlock lastBlock = (DataBlock) dataMap.get(dataMap.lastKey());
415 dataBlock = new RawDataBlock(lastBlock.getAddress() + ((lastBlock.getSize() + 3) & ~3), size);
416 }
417 dataMap.put(new Integer(dataBlock.getAddress()), dataBlock);
418 currentErrorBlock = dataBlock;
419 return dataBlock;
420 }
421
422 public final void storeString0(int address, String s) {
423 DataBlock block = findStringBlock(address, s);
424 ByteArrayUtils.putString0(block.getBytes(), address - block.getAddress(), s);
425 }
426
427 public final void storeStringCR(int address, String s) {
428 DataBlock block = findStringBlock(address, s);
429 ByteArrayUtils.putStringCR(block.getBytes(), address - block.getAddress(), s);
430 }
431
432 private DataBlock findStringBlock(int address, String string) {
433 final int length = string.length() + 1;
434 DataBlock block = findDataBlock(address, length);
435 if (block == null) {
436 block = createDataBlock(address, length);
437 }
438 return block;
439 }
440
441 public Rectangle getRectangle(int address) {
442 DataBlock block = findDataBlock(address, 16);
443 if (block == null) {
444 throw new AddressException(address);
445 }
446 return ByteArrayUtils.getRectangle(block.getBytes(), address - block.getAddress());
447 }
448
449 public Point getPoint(int address) {
450 DataBlock block = findDataBlock(address, 8);
451 if (block == null) {
452 throw new AddressException(address);
453 }
454 return ByteArrayUtils.getPoint(block.getBytes(), address - block.getAddress());
455 }
456
457 public void storePoint(int address, Point p) {
458 DataBlock block = findDataBlock(address, 8);
459 if (block == null) {
460 throw new AddressException(address);
461 }
462 ByteArrayUtils.putPoint(block.getBytes(), address - block.getAddress(), p);
463 }
464
465 public void storeRectangle(int address, Rectangle r) {
466 DataBlock block = findDataBlock(address, 8);
467 if (block == null) {
468 throw new AddressException(address);
469 }
470 ByteArrayUtils.putRectangle(block.getBytes(), address - block.getAddress(), r);
471 }
472 }