Fix emulation bug with self-modifying code
[jpcrr.git] / org / jpc / emulator / memory / LazyCodeBlockMemory.java
blob3a03d29beea44bff9a7e01081e9b5f706cf2bb55
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.emulator.memory;
32 import org.jpc.emulator.StatusDumper;
33 import org.jpc.emulator.SRLoader;
34 import org.jpc.emulator.SRDumper;
35 import java.util.Arrays;
36 import java.io.*;
37 import org.jpc.emulator.memory.codeblock.*;
38 import org.jpc.emulator.processor.Processor;
40 /**
41 * <code>Memory</code> object with simple execute capabilities. Uses a
42 * {@link org.jpc.emulator.memory.codeblock.CodeBlockManager} instance to generate
43 * {@link org.jpc.emulator.memory.codeblock.CodeBlock} objects which are then
44 * stored in mirror arrays of the memory structure.
45 * @author Chris Dennis
46 * @author Rhys Newman
47 * @author Ian Preston
49 public class LazyCodeBlockMemory extends AbstractMemory {
51 private CodeBlockManager codeBlockManager;
52 private static final BlankCodeBlock PLACEHOLDER = new BlankCodeBlock();
53 private RealModeCodeBlock[] realCodeBuffer;
54 private ProtectedModeCodeBlock[] protectedCodeBuffer;
55 private Virtual8086ModeCodeBlock[] virtual8086CodeBuffer;
56 private static final int ALLOCATION_THRESHOLD = 10;
57 private final int size;
58 private byte[] buffer = null;
59 private int nullReadCount = 0;
60 private boolean fpuHackFlag;
62 public void setFPUHack()
64 fpuHackFlag = true;
67 public boolean isDirty()
69 return (buffer != null);
72 public void dumpStatusPartial(StatusDumper output)
74 super.dumpStatusPartial(output);
75 output.println("\tsize " + size + " nullReadCount " + nullReadCount);
76 output.println("\tbuffer:");
77 output.printArray(buffer, "buffer");
78 //Skip the codeblocks. They are cache.
81 public void dumpStatus(StatusDumper output)
83 if(output.dumped(this))
84 return;
86 output.println("#" + output.objectNumber(this) + ": LazyCodeBlockMemory:");
87 dumpStatusPartial(output);
88 output.endObject();
91 public void dumpSRPartial(SRDumper output) throws IOException
93 super.dumpSRPartial(output);
94 output.dumpInt(size);
95 output.dumpArray(buffer);
96 output.dumpInt(nullReadCount);
97 output.dumpObject(codeBlockManager);
98 output.dumpBoolean(fpuHackFlag);
99 //Skip the codeblocks. They are cache.
102 public LazyCodeBlockMemory(SRLoader input) throws IOException
104 super(input);
105 size = input.loadInt();
106 buffer = input.loadArrayByte();
107 nullReadCount = input.loadInt();
108 codeBlockManager = (CodeBlockManager)input.loadObject();
109 fpuHackFlag = false;
110 if(input.objectEndsHere())
111 return;
112 fpuHackFlag = input.loadBoolean();
116 * Constructs an instance <code>size</code> bytes long.
117 * @param size
119 public LazyCodeBlockMemory(int size, CodeBlockManager manager) {
120 this.size = size;
121 this.codeBlockManager = manager;
125 * Should probably be made private.
127 protected void constructCodeBlocksArray() {
128 realCodeBuffer = new RealModeCodeBlock[(int) getSize()];
129 protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()];
130 virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()];
133 private void constructRealCodeBlocksArray() {
134 realCodeBuffer = new RealModeCodeBlock[(int) getSize()];
137 private void constructVirtual8086CodeBlocksArray() {
138 virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()];
141 private void constructProtectedCodeBlocksArray() {
142 protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()];
145 public int executeProtected(Processor cpu, int offset) {
146 int x86Count = 0;
147 int ip = cpu.getInstructionPointer();
149 offset = ip & AddressSpace.BLOCK_MASK;
150 ProtectedModeCodeBlock block = getProtectedModeCodeBlockAt(offset);
155 x86Count += block.execute(cpu);
157 catch (NullPointerException e)
159 block = codeBlockManager.getProtectedModeCodeBlockAt(this, offset, cpu.cs.getDefaultSizeFlag());
160 setProtectedCodeBlockAt(offset, block);
161 x86Count += block.execute(cpu);
164 catch (CodeBlockReplacementException e)
166 block = (ProtectedModeCodeBlock) e.getReplacement();
167 protectedCodeBuffer[offset] = block;
168 x86Count += block.execute(cpu);
171 return x86Count;
174 public int executeReal(Processor cpu, int offset) {
175 int x86Count = 0;
176 int ip = cpu.getInstructionPointer();
178 offset = ip & AddressSpace.BLOCK_MASK;
179 RealModeCodeBlock block = getRealModeCodeBlockAt(offset);
184 x86Count += block.execute(cpu);
186 catch (NullPointerException e)
188 block = codeBlockManager.getRealModeCodeBlockAt(this, offset);
189 setRealCodeBlockAt(offset, block);
190 x86Count += block.execute(cpu);
193 catch (CodeBlockReplacementException e)
195 block = (RealModeCodeBlock) e.getReplacement();
196 realCodeBuffer[offset] = block;
197 x86Count += block.execute(cpu);
200 return x86Count;
203 public int executeVirtual8086(Processor cpu, int offset) {
204 int x86Count = 0;
205 int ip = cpu.getInstructionPointer();
207 offset = ip & AddressSpace.BLOCK_MASK;
208 Virtual8086ModeCodeBlock block = getVirtual8086ModeCodeBlockAt(offset);
213 x86Count += block.execute(cpu);
215 catch (NullPointerException e)
217 block = codeBlockManager.getVirtual8086ModeCodeBlockAt(this, offset);
218 setVirtual8086CodeBlockAt(offset, block);
219 x86Count += block.execute(cpu);
222 catch (CodeBlockReplacementException e)
224 block = (Virtual8086ModeCodeBlock) e.getReplacement();
225 virtual8086CodeBuffer[offset] = block;
226 x86Count += block.execute(cpu);
229 return x86Count;
232 private RealModeCodeBlock getRealModeCodeBlockAt(int offset) {
233 try {
234 return realCodeBuffer[offset];
235 } catch (NullPointerException e) {
236 constructRealCodeBlocksArray();
237 return realCodeBuffer[offset];
241 private ProtectedModeCodeBlock getProtectedModeCodeBlockAt(int offset) {
242 try {
243 return protectedCodeBuffer[offset];
244 } catch (NullPointerException e) {
245 constructProtectedCodeBlocksArray();
246 return protectedCodeBuffer[offset];
250 private Virtual8086ModeCodeBlock getVirtual8086ModeCodeBlockAt(int offset) {
251 try {
252 return virtual8086CodeBuffer[offset];
253 } catch (NullPointerException e) {
254 constructVirtual8086CodeBlocksArray();
255 return virtual8086CodeBuffer[offset];
259 private void removeVirtual8086CodeBlockAt(int offset)
261 Virtual8086ModeCodeBlock b = virtual8086CodeBuffer[offset];
262 if((b == null) || (b == PLACEHOLDER))
263 return;
265 virtual8086CodeBuffer[offset] = null;
266 int len = b.getX86Length();
267 for(int i = offset + 1; (i < offset + len) && (i < virtual8086CodeBuffer.length); i++)
268 if(virtual8086CodeBuffer[i] == PLACEHOLDER)
269 virtual8086CodeBuffer[i] = null;
271 for(int i = Math.min(offset + len, virtual8086CodeBuffer.length) - 1; i >= 0; i--) {
272 if(virtual8086CodeBuffer[i] == null) {
273 if(i < offset)
274 break;
275 else
276 continue;
278 if(virtual8086CodeBuffer[i] == PLACEHOLDER)
279 continue;
281 Virtual8086ModeCodeBlock bb = virtual8086CodeBuffer[i];
282 len = bb.getX86Length();
284 for(int j = i + 1; (j < i + len) && (j < virtual8086CodeBuffer.length); j++)
285 if(virtual8086CodeBuffer[j] == null)
286 virtual8086CodeBuffer[j] = PLACEHOLDER;
290 private void removeProtectedCodeBlockAt(int offset)
292 ProtectedModeCodeBlock b = protectedCodeBuffer[offset];
293 if((b == null) || (b == PLACEHOLDER))
294 return;
296 protectedCodeBuffer[offset] = null;
297 int len = b.getX86Length();
298 for(int i = offset + 1; (i < offset + len) && (i < protectedCodeBuffer.length); i++)
299 if(protectedCodeBuffer[i] == PLACEHOLDER)
300 protectedCodeBuffer[i] = null;
302 for(int i = Math.min(offset + len, protectedCodeBuffer.length) - 1; i >= 0; i--) {
303 if(protectedCodeBuffer[i] == null) {
304 if(i < offset)
305 break;
306 else
307 continue;
309 if(protectedCodeBuffer[i] == PLACEHOLDER)
310 continue;
312 ProtectedModeCodeBlock bb = protectedCodeBuffer[i];
313 len = bb.getX86Length();
315 for(int j = i + 1; (j < i + len) && (j < protectedCodeBuffer.length); j++)
316 if(protectedCodeBuffer[j] == null)
317 protectedCodeBuffer[j] = PLACEHOLDER;
321 private void removeRealCodeBlockAt(int offset)
323 RealModeCodeBlock b = realCodeBuffer[offset];
324 if((b == null) || (b == PLACEHOLDER))
325 return;
327 realCodeBuffer[offset] = null;
328 int len = b.getX86Length();
329 for(int i = offset + 1; (i < offset + len) && (i < realCodeBuffer.length); i++)
330 if(realCodeBuffer[i] == PLACEHOLDER)
331 realCodeBuffer[i] = null;
333 for(int i = Math.min(offset + len, realCodeBuffer.length) - 1; i >= 0; i--) {
334 if(realCodeBuffer[i] == null) {
335 if(i < offset)
336 break;
337 else
338 continue;
340 if(realCodeBuffer[i] == PLACEHOLDER)
341 continue;
343 RealModeCodeBlock bb = realCodeBuffer[i];
344 len = bb.getX86Length();
346 for(int j = i + 1; (j < i + len) && (j < realCodeBuffer.length); j++)
347 if(realCodeBuffer[j] == null)
348 realCodeBuffer[j] = PLACEHOLDER;
352 private void setVirtual8086CodeBlockAt(int offset, Virtual8086ModeCodeBlock block)
354 removeVirtual8086CodeBlockAt(offset);
355 if(block == null)
356 return;
358 virtual8086CodeBuffer[offset] = block;
359 int len = block.getX86Length();
360 for(int i = offset + 1; (i < offset + len) && (i < virtual8086CodeBuffer.length); i++)
361 if(virtual8086CodeBuffer[i] == null)
362 virtual8086CodeBuffer[i] = PLACEHOLDER;
365 private void setProtectedCodeBlockAt(int offset, ProtectedModeCodeBlock block)
367 removeProtectedCodeBlockAt(offset);
368 if(block == null)
369 return;
371 protectedCodeBuffer[offset] = block;
372 int len = block.getX86Length();
373 for(int i = offset + 1; (i < offset + len) && (i < protectedCodeBuffer.length); i++)
374 if(protectedCodeBuffer[i] == null)
375 protectedCodeBuffer[i] = PLACEHOLDER;
378 private void setRealCodeBlockAt(int offset, RealModeCodeBlock block)
380 removeRealCodeBlockAt(offset);
381 if(block == null)
382 return;
384 realCodeBuffer[offset] = block;
385 int len = block.getX86Length();
386 for(int i = offset + 1; (i < offset + len) && (i < realCodeBuffer.length); i++)
387 if(realCodeBuffer[i] == null)
388 realCodeBuffer[i] = PLACEHOLDER;
391 private void regionAltered(int start, int end) {
392 if(realCodeBuffer != null) {
393 for(int i = end; i >= 0; i--) {
394 RealModeCodeBlock b = realCodeBuffer[i];
395 if(b == null) {
396 if(i < start)
397 break;
398 else
399 continue;
402 if(b == PLACEHOLDER)
403 continue;
405 if(!b.handleMemoryRegionChange(start, end)) {
406 removeRealCodeBlockAt(i);
407 b.invalidate();
412 if(protectedCodeBuffer != null) {
413 for(int i = end; i >= 0; i--) {
414 ProtectedModeCodeBlock b = protectedCodeBuffer[i];
415 if(b == null) {
416 if(i < start)
417 break;
418 else
419 continue;
422 if(b == PLACEHOLDER)
423 continue;
425 if(!b.handleMemoryRegionChange(start, end)) {
426 removeProtectedCodeBlockAt(i);
427 b.invalidate();
432 if(virtual8086CodeBuffer != null) {
433 for(int i = end; i >= 0; i--) {
434 Virtual8086ModeCodeBlock b = virtual8086CodeBuffer[i];
435 if(b == null) {
436 if(i < start)
437 break;
438 else
439 continue;
442 if(b == PLACEHOLDER)
443 continue;
445 if(!b.handleMemoryRegionChange(start, end)) {
446 removeVirtual8086CodeBlockAt(i);
447 b.invalidate();
453 public void clear()
455 realCodeBuffer = null;
456 protectedCodeBuffer = null;
457 virtual8086CodeBuffer = null;
458 buffer = null;
461 public String toString()
463 return "LazyCodeBlockMemory[" + getSize() + "]";
466 //This class does not need to be dumpable because codeblocks can't be saved.
467 private static class BlankCodeBlock implements RealModeCodeBlock, ProtectedModeCodeBlock, Virtual8086ModeCodeBlock
469 private static final RuntimeException executeException = new NullPointerException();
471 public int getX86Length()
473 return 0;
476 public int getX86Count()
478 return 0;
481 public int execute(Processor cpu)
483 throw executeException;
486 public void invalidate()
490 public boolean handleMemoryRegionChange(int startAddress, int endAddress)
492 return false;
495 public String getDisplayString()
497 return "\n\n<<Blank Block>>\n\n";
500 public String toString()
502 return " -- Blank --\n";
506 public ProtectedModeCodeBlock getProtectedBlock(int offset, boolean size)
508 if(protectedCodeBuffer == null) {
509 allocateBuffer();
510 protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()];
512 ProtectedModeCodeBlock block = protectedCodeBuffer[offset];
513 if((block != null) && (block != PLACEHOLDER))
514 return block;
516 block = codeBlockManager.getProtectedModeCodeBlockAt(this, offset, size);
517 setProtectedCodeBlockAt(offset, block);
518 return block;
521 public Virtual8086ModeCodeBlock getVirtual8086Block(int offset)
523 if(virtual8086CodeBuffer == null) {
524 allocateBuffer();
525 virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()];
527 Virtual8086ModeCodeBlock block = virtual8086CodeBuffer[offset];
528 if((block != null) && (block != PLACEHOLDER))
529 return block;
531 block = codeBlockManager.getVirtual8086ModeCodeBlockAt(this, offset);
532 setVirtual8086CodeBlockAt(offset, block);
533 return block;
536 public RealModeCodeBlock getRealBlock(int offset)
538 if(realCodeBuffer == null) {
539 allocateBuffer();
540 realCodeBuffer = new RealModeCodeBlock[(int) getSize()];
542 RealModeCodeBlock block = realCodeBuffer[offset];
543 if((block != null) && (block != PLACEHOLDER))
544 return block;
546 block = codeBlockManager.getRealModeCodeBlockAt(this, offset);
547 setRealCodeBlockAt(offset, block);
548 return block;
551 //begin lazy memory methods
552 private final void allocateBuffer()
554 if(buffer == null)
555 buffer = new byte[size];
558 public void copyContentsIntoArray(int address, byte[] buf, int off, int len)
560 try {
561 System.arraycopy(buffer, address, buf, off, len);
562 } catch (NullPointerException e) {
563 if(++nullReadCount == ALLOCATION_THRESHOLD) {
564 allocateBuffer();
565 System.arraycopy(buffer, address, buf, off, len);
566 } else
567 Arrays.fill(buf, off, off + len, (byte) 0);
571 public void loadInitialContents(int address, byte[] buf, int off, int len)
573 try {
574 System.arraycopy(buf, off, buffer, address, len);
575 } catch (NullPointerException e) {
576 allocateBuffer();
577 System.arraycopy(buf, off, buffer, address, len);
581 public void copyArrayIntoContents(int address, byte[] buf, int off, int len)
583 try {
584 System.arraycopy(buf, off, buffer, address, len);
585 } catch (NullPointerException e) {
586 allocateBuffer();
587 System.arraycopy(buf, off, buffer, address, len);
589 regionAltered(address, address + len - 1);
592 public long getSize()
594 return size;
597 public boolean isAllocated()
599 return (buffer != null);
602 public byte getByte(int offset)
604 try {
605 if(fpuHackFlag && offset == 0x410) return (byte)(buffer[offset] & 0xFD);
607 return buffer[offset];
608 } catch (NullPointerException e) {
609 if(++nullReadCount == ALLOCATION_THRESHOLD) {
610 allocateBuffer();
611 return buffer[offset];
612 } else
613 return 0;
617 public void setByte(int offset, byte data) {
618 if(getByte(offset) == data)
619 return;
620 try {
621 buffer[offset] = data;
622 } catch (NullPointerException e) {
623 allocateBuffer();
624 buffer[offset] = data;
626 regionAltered(offset, offset);
629 public short getWord(int offset) {
630 try {
631 int result = 0xFF & buffer[offset];
632 offset++;
633 result |= buffer[offset] << 8;
635 if(fpuHackFlag && offset == 0x411) result &= 0xFFFD;
636 if(fpuHackFlag && offset == 0x410) result &= 0xFDFF;
638 return (short) result;
639 } catch (NullPointerException e) {
640 if(++nullReadCount == ALLOCATION_THRESHOLD) {
641 allocateBuffer();
642 int result = 0xFF & buffer[offset];
643 offset++;
644 result |= buffer[offset] << 8;
645 return (short) result;
646 } else
647 return 0;
651 public int getDoubleWord(int offset) {
652 try {
653 int result = 0xFF & buffer[offset];
654 offset++;
655 result |= (0xFF & buffer[offset]) << 8;
656 offset++;
657 result |= (0xFF & buffer[offset]) << 16;
658 offset++;
659 result |= (buffer[offset]) << 24;
661 if(fpuHackFlag && offset == 0x413) result &= 0xFFFFFFFD;
662 if(fpuHackFlag && offset == 0x412) result &= 0xFFFFFDFF;
663 if(fpuHackFlag && offset == 0x411) result &= 0xFFFDFFFF;
664 if(fpuHackFlag && offset == 0x410) result &= 0xFDFFFFFF;
666 return result;
667 } catch (NullPointerException e) {
668 if(++nullReadCount == ALLOCATION_THRESHOLD) {
669 allocateBuffer();
670 int result = 0xFF & buffer[offset];
671 offset++;
672 result |= (0xFF & buffer[offset]) << 8;
673 offset++;
674 result |= (0xFF & buffer[offset]) << 16;
675 offset++;
676 result |= (buffer[offset]) << 24;
677 return result;
678 } else
679 return 0;
683 public void setWord(int offset, short data) {
684 if(getWord(offset) == data)
685 return;
686 try {
687 buffer[offset] = (byte) data;
688 offset++;
689 buffer[offset] = (byte) (data >> 8);
690 } catch (NullPointerException e) {
691 allocateBuffer();
692 buffer[offset] = (byte) data;
693 offset++;
694 buffer[offset] = (byte) (data >> 8);
696 regionAltered(offset, offset + 1);
699 public void setDoubleWord(int offset, int data) {
700 if(getDoubleWord(offset) == data)
701 return;
702 try {
703 buffer[offset] = (byte) data;
704 offset++;
705 data >>= 8;
706 buffer[offset] = (byte) (data);
707 offset++;
708 data >>= 8;
709 buffer[offset] = (byte) (data);
710 offset++;
711 data >>= 8;
712 buffer[offset] = (byte) (data);
713 } catch (NullPointerException e) {
714 allocateBuffer();
715 buffer[offset] = (byte) data;
716 offset++;
717 data >>= 8;
718 buffer[offset] = (byte) (data);
719 offset++;
720 data >>= 8;
721 buffer[offset] = (byte) (data);
722 offset++;
723 data >>= 8;
724 buffer[offset] = (byte) (data);
726 regionAltered(offset, offset + 3);