View Javadoc

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              // Create a new block
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()) { // Faster version if its in the original app image, most likely.
120             // NB We cannot fetch from the original byte array because it may have been replaced in the CodeBlock
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         // Always shifts address of block, if it's not the last one and it's getting bigger
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                 // Scan for a zero-byte
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                     // We got the whole string.
276                     return buffer.toString();
277                 }
278                 // We ran off the end of the byte array
279                 address = block.getAddress() + bytes.length;
280             } while (true);
281         } catch (UnsupportedEncodingException e) {
282             // Will not happen!
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     // Loads throw an exception if the address is not already in the map
321     // Note that the image is mapped into the dataMap in the constructor.
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     // Stores will automatically map the address into the dataMap if it is not already there.
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 }