/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Copyright (C) 2012-2013 Ian Preston
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
End of licence header
*/
package org.jpc.emulator.memory;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class SlowTLB extends TLB
{
private static final byte FOUR_M = (byte) 0x01;
private static final byte FOUR_K = (byte) 0x00;
private final Set<Integer> nonGlobalPages;
private Memory[] readUserIndex, readSupervisorIndex, writeUserIndex, writeSupervisorIndex, readIndex, writeIndex;
private byte[] pageSize;
private boolean globalPagesEnabled;
public SlowTLB()
{
globalPagesEnabled = false;
nonGlobalPages = new HashSet<Integer>();
pageSize = new byte[AddressSpace.INDEX_SIZE];
for (int i=0; i < AddressSpace.INDEX_SIZE; i++)
pageSize[i] = FOUR_K;
}
public void saveState(DataOutput output) throws IOException
{
output.writeInt(pageSize.length);
output.write(pageSize);
output.writeInt(nonGlobalPages.size());
for (Integer value : nonGlobalPages)
output.writeInt(value.intValue());
}
public void loadState(DataInput input) throws IOException
{
int len = input.readInt();
pageSize = new byte[len];
input.readFully(pageSize,0,len);
nonGlobalPages.clear();
int count = input.readInt();
for (int i=0; i < count; i++)
nonGlobalPages.add(Integer.valueOf(input.readInt()));
}
public void setSupervisor(boolean isSupervisor)
{
if (isSupervisor)
{
readIndex = readSupervisorIndex;
writeIndex = writeSupervisorIndex;
}
else
{
readIndex = readUserIndex;
writeIndex = writeUserIndex;
}
}
public void setWriteProtectPages(boolean value)
{
if (value) {
if (writeSupervisorIndex != null)
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
nullIndex(writeSupervisorIndex, i);
}
}
private Memory[] createReadIndex(boolean isSupervisor)
{
if (isSupervisor)
return (readIndex = readSupervisorIndex = new Memory[AddressSpace.INDEX_SIZE]);
else
return (readIndex = readUserIndex = new Memory[AddressSpace.INDEX_SIZE]);
}
private Memory[] createWriteIndex(boolean isSupervisor)
{
if (isSupervisor)
return (writeIndex = writeSupervisorIndex = new Memory[AddressSpace.INDEX_SIZE]);
else
return (writeIndex = writeUserIndex = new Memory[AddressSpace.INDEX_SIZE]);
}
/**
* Invalidate any entries for this address in the translation cache.
* <p>
* This will cause the next request for an address within the same page to
* have to walk the translation tables in memory.
* @param offset address within the page to be invalidated.
*/
public void invalidateTLBEntry(int offset)
{
int index = offset >>> AddressSpace.INDEX_SHIFT;
if (pageSize[index] == FOUR_K) {
nullIndex(readSupervisorIndex, index);
nullIndex(writeSupervisorIndex, index);
nullIndex(readUserIndex, index);
nullIndex(writeUserIndex, index);
nonGlobalPages.remove(Integer.valueOf(index));
} else {
index &= 0xFFC00;
for (int i = 0; i < 1024; i++, index++) {
nullIndex(readSupervisorIndex, index);
nullIndex(writeSupervisorIndex, index);
nullIndex(readUserIndex, index);
nullIndex(writeUserIndex, index);
nonGlobalPages.remove(Integer.valueOf(index));
}
}
}
private static void nullIndex(Memory[] array, int index)
{
try {
array[index] = null;
} catch (NullPointerException e) {}
}
public void flush()
{
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
pageSize[i] = FOUR_K;
nonGlobalPages.clear();
readUserIndex = null;
writeUserIndex = null;
readSupervisorIndex = null;
writeSupervisorIndex = null;
readIndex = null;
writeIndex = null;
}
public void flushNonGlobal()
{
if (globalPagesEnabled) {
for (Integer value : nonGlobalPages) {
int index = value.intValue();
nullIndex(readSupervisorIndex, index);
nullIndex(writeSupervisorIndex, index);
nullIndex(readUserIndex, index);
nullIndex(writeUserIndex, index);
pageSize[index] = FOUR_K;
}
nonGlobalPages.clear();
} else
flush();
}
@Override
public void setGlobalPages(boolean enabled) {
globalPagesEnabled = enabled;
}
public void addNonGlobalPage(int addr) {
nonGlobalPages.add(addr >>> AddressSpace.INDEX_SHIFT);
}
@Override
public boolean globalPagesEnabled() {
return globalPagesEnabled;
}
@Override
public Memory getReadMemoryBlockAt(boolean isSupervisor, int addr) {
return getReadIndexValue(isSupervisor, addr >>> AddressSpace.INDEX_SHIFT);
}
@Override
public void setReadMemoryBlockAt(boolean isSupervisor, int addr, Memory value) {
int index = addr >>> AddressSpace.INDEX_SHIFT;
try {
readIndex[index] = value;
} catch (NullPointerException e) {
createReadIndex(isSupervisor)[index] = value;
}
}
@Override
public Memory getWriteMemoryBlockAt(boolean isSupervisor, int addr) {
return getWriteIndexValue(isSupervisor, addr >>> AddressSpace.INDEX_SHIFT);
}
private Memory getReadIndexValue(boolean isSupervisor, int index)
{
try {
return readIndex[index];
} catch (NullPointerException e) {
return createReadIndex(isSupervisor)[index];
}
}
@Override
public void setWriteMemoryBlockAt(boolean isSupervisor, int addr, Memory value) {
int index = addr >>> AddressSpace.INDEX_SHIFT;
try {
writeIndex[index] = value;
} catch (NullPointerException e) {
createWriteIndex(isSupervisor)[index] = value;
}
}
private Memory getWriteIndexValue(boolean isSupervisor, int index)
{
try {
return writeIndex[index];
} catch (NullPointerException e) {
return createWriteIndex(isSupervisor)[index];
}
}
protected void setPageSize(int addr, byte type)
{
pageSize[addr >>> AddressSpace.INDEX_SHIFT] = type;
}
protected void replaceBlocks(Memory oldBlock, Memory newBlock)
{
try {
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
if (readUserIndex[i] == oldBlock)
readUserIndex[i] = newBlock;
} catch (NullPointerException e) {}
try {
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
if (writeUserIndex[i] == oldBlock)
writeUserIndex[i] = newBlock;
} catch (NullPointerException e) {}
try {
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
if (readSupervisorIndex[i] == oldBlock)
readSupervisorIndex[i] = newBlock;
} catch (NullPointerException e) {}
try {
for (int i = 0; i < AddressSpace.INDEX_SIZE; i++)
if (writeSupervisorIndex[i] == oldBlock)
writeSupervisorIndex[i] = newBlock;
} catch (NullPointerException e) {}
}
}