/*
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 FastTLB extends TLB
{
private static final byte FOUR_M = (byte) 0x01;
private static final byte FOUR_K = (byte) 0x00;
private static final int TLB_SIZE = 1024;
private static final int TLB_MASK = (TLB_SIZE-1) << 12;
private static int TLBIndexOf(int addr) {
return (addr & TLB_MASK) >>> 12;
}
private final Set<Integer> nonGlobalPages = new HashSet<Integer>();
private TLB_Entry[] cache = new TLB_Entry[TLB_SIZE];
private boolean split_large = false;
private boolean globalPagesEnabled;
private byte[] pageSize;
public FastTLB()
{
pageSize = new byte[AddressSpace.INDEX_SIZE];
for (int i=0; i < AddressSpace.INDEX_SIZE; i++)
pageSize[i] = FOUR_K;
}
@Override
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());
}
@Override
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()));
}
@Override
public void setSupervisor(boolean isSupervisor) {
}
@Override
public void setWriteProtectPages(boolean value) {
}
@Override
public void flush() {
cache = new TLB_Entry[TLB_SIZE];
}
@Override
public void flushNonGlobal() {
if (globalPagesEnabled) {
for (Integer value : nonGlobalPages) {
int page = value.intValue();
int index = TLBIndexOf(page << AddressSpace.INDEX_SHIFT);
if ((cache[index] == null) || (!cache[index].samePage(page)))
continue;
cache[index] = null;
pageSize[index] = FOUR_K;
}
nonGlobalPages.clear();
} else
flush();
}
@Override
public void setGlobalPages(boolean enabled) {
globalPagesEnabled = enabled;
}
@Override
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) {
TLB_Entry entry = cache[TLBIndexOf(addr)];
if ((entry == null) || !entry.samePage(addr) || !entry.isRead(isSupervisor))
return null;
return entry.m;
}
@Override
public void setReadMemoryBlockAt(boolean isSupervisor, int addr, Memory m) {
int index = TLBIndexOf(addr);
if ((cache[index] == null) || !cache[index].samePage(addr))
cache[index] = new TLB_Entry(m, addr & AddressSpace.INDEX_MASK, TLB_Entry.getAccess(isSupervisor, true, false, true));
else
cache[index].accessBits |= TLB_Entry.getAccess(isSupervisor, true, false, true);
}
@Override
public Memory getWriteMemoryBlockAt(boolean isSupervisor, int addr) {
TLB_Entry entry = cache[TLBIndexOf(addr)];
if ((entry == null) || !entry.samePage(addr) || !entry.isWrite(isSupervisor))
return null;
return entry.m;
}
@Override
public void setWriteMemoryBlockAt(boolean isSupervisor, int addr, Memory m) {
int index = TLBIndexOf(addr);
if ((cache[index] == null) || !cache[index].samePage(addr))
cache[index] = new TLB_Entry(m, addr & AddressSpace.INDEX_MASK, TLB_Entry.getAccess(isSupervisor, false, true, false));
else
cache[index].accessBits |= TLB_Entry.getAccess(isSupervisor, false, true, false);
}
@Override
protected void setPageSize(int addr, byte type) {
pageSize[addr >>> AddressSpace.INDEX_SHIFT] = type;
}
@Override
protected void replaceBlocks(Memory oldBlock, Memory newBlock) {
System.out.println("Replace blocks used!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
@Override
public void invalidateTLBEntry(int addr) {
int index = addr >>> AddressSpace.INDEX_SHIFT;
if (pageSize[index] == FOUR_K) {
nonGlobalPages.remove(Integer.valueOf(index));
int page = TLBIndexOf(addr);
if ((cache[page] == null) || (!cache[page].samePage(addr)))
return;
cache[page] = null;
} else {
index &= 0xFFC00;
for (int i = 0; i < 1024; i++, index++) {
nonGlobalPages.remove(Integer.valueOf(index));
int page = TLBIndexOf(index << AddressSpace.INDEX_SHIFT);
if ((cache[page] == null) || (!cache[page].samePage(addr)))
continue;
cache[page] = null;
}
}
}
private static class TLB_Entry
{
private static int GLOBAL_PAGE = 0x80000000;
private static int SysReadOK = 0x01;
private static int UserReadOK = 0x02;
private static int SysWriteOK = 0x04;
private static int UserWriteOK = 0x08;
private static int SysExecuteOK = 0x10;
private static int UserExecuteOK = 0x20;
private static final boolean[] allowed = new boolean[32];
static {
for (int i=0; i < 32; i++)
allowed[i] = (0xff0bbb0b & (1 << i)) != 0;
}
private static int getAccess(boolean isSupervisor, boolean readable, boolean writable, boolean executable)
{
if (isSupervisor)
return (readable ? SysReadOK : 0) | (writable ? SysWriteOK : 0) | (executable ? SysExecuteOK : 0);
else
return (readable ? SysReadOK | UserReadOK : 0) | (writable ? SysWriteOK | UserWriteOK : 0) | (executable ? SysExecuteOK | UserExecuteOK : 0);
}
int linearAddress;
Memory m;
int accessBits;
int linearMask;
private TLB_Entry(Memory m, int linearAddress, int accessBits)
{
this.linearAddress = linearAddress;
this.m = m;
this.accessBits = accessBits;
linearMask = ~0xfff;
}
public boolean isRead(boolean isSupervisor)
{
if (isSupervisor)
return (accessBits & SysReadOK) != 0;
return (accessBits & UserReadOK) != 0;
}
public boolean isWrite(boolean isSupervisor)
{
if (isSupervisor)
return (accessBits & SysWriteOK) != 0;
return (accessBits & UserWriteOK) != 0;
}
public boolean samePage(int addr)
{
return (linearAddress & linearMask) == (addr & linearMask);
}
}
}