/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.std.memory;
import java.util.Arrays;
import com.cburch.hex.HexModel;
import com.cburch.hex.HexModelListener;
import com.cburch.logisim.util.EventSourceWeakSupport;
class MemContents implements Cloneable, HexModel {
static MemContents create(int addrBits, int width) {
return new MemContents(addrBits, width);
}
private static final int PAGE_SIZE_BITS = 12;
private static final int PAGE_SIZE = 1 << PAGE_SIZE_BITS;
private static final int PAGE_MASK = PAGE_SIZE - 1;
private EventSourceWeakSupport<HexModelListener> listeners = null;
private int width;
private int addrBits;
private int mask;
private MemContentsSub.ContentsInterface[] pages;
private MemContents(int addrBits, int width) {
listeners = null;
setDimensions(addrBits, width);
}
//
// HexModel methods
//
public void addHexModelListener(HexModelListener l) {
if (listeners == null)
listeners = new EventSourceWeakSupport<HexModelListener>();
listeners.add(l);
}
public void clear() {
for (int i = 0; i < pages.length; i++) {
if (pages[i] != null) {
if (pages[i] != null)
clearPage(i);
}
}
}
private void clearPage(int index) {
MemContentsSub.ContentsInterface page = pages[index];
int[] oldValues = new int[page.getLength()];
boolean changed = false;
for (int j = 0; j < oldValues.length; j++) {
int val = page.get(j) & mask;
oldValues[j] = val;
if (val != 0)
changed = true;
}
if (changed) {
pages[index] = null;
fireBytesChanged(index << PAGE_SIZE_BITS, oldValues.length,
oldValues);
}
}
//
// other methods
//
@Override
public MemContents clone() {
try {
MemContents ret = (MemContents) super.clone();
ret.listeners = null;
ret.pages = new MemContentsSub.ContentsInterface[this.pages.length];
for (int i = 0; i < ret.pages.length; i++) {
if (this.pages[i] != null) {
ret.pages[i] = this.pages[i].clone();
}
}
return ret;
} catch (CloneNotSupportedException ex) {
return this;
}
}
private void ensurePage(int index) {
if (pages[index] == null) {
pages[index] = MemContentsSub.createContents(PAGE_SIZE, width);
}
}
public void fill(long start, long len, int value) {
if (len == 0)
return;
int pageStart = (int) (start >>> PAGE_SIZE_BITS);
int startOffs = (int) (start & PAGE_MASK);
int pageEnd = (int) ((start + len - 1) >>> PAGE_SIZE_BITS);
int endOffs = (int) ((start + len - 1) & PAGE_MASK);
value &= mask;
if (pageStart == pageEnd) {
ensurePage(pageStart);
int[] vals = new int[(int) len];
Arrays.fill(vals, value);
MemContentsSub.ContentsInterface page = pages[pageStart];
if (!page.matches(vals, startOffs, mask)) {
int[] oldValues = page.get(startOffs, (int) len);
page.load(startOffs, vals, mask);
if (value == 0 && page.isClear())
pages[pageStart] = null;
fireBytesChanged(start, len, oldValues);
}
} else {
if (startOffs == 0) {
pageStart--;
} else {
if (value == 0 && pages[pageStart] == null) {
// nothing to do
} else {
ensurePage(pageStart);
int[] vals = new int[PAGE_SIZE - startOffs];
Arrays.fill(vals, value);
MemContentsSub.ContentsInterface page = pages[pageStart];
if (!page.matches(vals, startOffs, mask)) {
int[] oldValues = page.get(startOffs, vals.length);
page.load(startOffs, vals, mask);
if (value == 0 && page.isClear())
pages[pageStart] = null;
fireBytesChanged(start, PAGE_SIZE - pageStart,
oldValues);
}
}
}
if (value == 0) {
for (int i = pageStart + 1; i < pageEnd; i++) {
if (pages[i] != null)
clearPage(i);
}
} else {
int[] vals = new int[PAGE_SIZE];
Arrays.fill(vals, value);
for (int i = pageStart + 1; i < pageEnd; i++) {
ensurePage(i);
MemContentsSub.ContentsInterface page = pages[i];
if (!page.matches(vals, 0, mask)) {
int[] oldValues = page.get(0, PAGE_SIZE);
page.load(0, vals, mask);
fireBytesChanged((long) i << PAGE_SIZE_BITS, PAGE_SIZE,
oldValues);
}
}
}
if (endOffs >= 0) {
MemContentsSub.ContentsInterface page = pages[pageEnd];
if (value == 0 && page == null) {
// nothing to do
} else {
ensurePage(pageEnd);
int[] vals = new int[endOffs + 1];
Arrays.fill(vals, value);
if (!page.matches(vals, 0, mask)) {
int[] oldValues = page.get(0, endOffs + 1);
page.load(0, vals, mask);
if (value == 0 && page.isClear())
pages[pageEnd] = null;
fireBytesChanged((long) pageEnd << PAGE_SIZE_BITS,
endOffs + 1, oldValues);
}
}
}
}
}
private void fireBytesChanged(long start, long numBytes, int[] oldValues) {
if (listeners == null)
return;
boolean found = false;
for (HexModelListener l : listeners) {
found = true;
l.bytesChanged(this, start, numBytes, oldValues);
}
if (!found)
listeners = null;
}
private void fireMetainfoChanged() {
if (listeners == null)
return;
boolean found = false;
for (HexModelListener l : listeners) {
found = true;
l.metainfoChanged(this);
}
if (!found)
listeners = null;
}
public int get(long addr) {
int page = (int) (addr >>> PAGE_SIZE_BITS);
int offs = (int) (addr & PAGE_MASK);
if (page < 0 || page >= pages.length || pages[page] == null)
return 0;
return pages[page].get(offs) & mask;
}
public long getFirstOffset() {
return 0;
}
public long getLastOffset() {
return (1L << addrBits) - 1;
}
public int getLogLength() {
return addrBits;
}
public int getValueWidth() {
return width;
}
public int getWidth() {
return width;
}
public boolean isClear() {
for (int i = 0; i < pages.length; i++) {
MemContentsSub.ContentsInterface page = pages[i];
if (page != null) {
for (int j = page.getLength() - 1; j >= 0; j--) {
if (page.get(j) != 0)
return false;
}
}
}
return true;
}
public void removeHexModelListener(HexModelListener l) {
if (listeners == null)
return;
listeners.add(l);
if (listeners.isEmpty())
listeners = null;
}
public void set(long addr, int value) {
int page = (int) (addr >>> PAGE_SIZE_BITS);
int offs = (int) (addr & PAGE_MASK);
int old = pages[page] == null ? 0 : pages[page].get(offs) & mask;
int val = value & mask;
if (old != val) {
if (pages[page] == null) {
pages[page] = MemContentsSub.createContents(PAGE_SIZE, width);
}
pages[page].set(offs, val);
fireBytesChanged(addr, 1, new int[] { old });
}
}
public void set(long start, int[] values) {
if (values.length == 0)
return;
int pageStart = (int) (start >>> PAGE_SIZE_BITS);
int startOffs = (int) (start & PAGE_MASK);
int pageEnd = (int) ((start + values.length - 1) >>> PAGE_SIZE_BITS);
int endOffs = (int) ((start + values.length - 1) & PAGE_MASK);
if (pageStart == pageEnd) {
ensurePage(pageStart);
MemContentsSub.ContentsInterface page = pages[pageStart];
if (!page.matches(values, startOffs, mask)) {
int[] oldValues = page.get(startOffs, values.length);
page.load(startOffs, values, mask);
if (page.isClear())
pages[pageStart] = null;
fireBytesChanged(start, values.length, oldValues);
}
} else {
int nextOffs;
if (startOffs == 0) {
pageStart--;
nextOffs = 0;
} else {
ensurePage(pageStart);
int[] vals = new int[PAGE_SIZE - startOffs];
System.arraycopy(values, 0, vals, 0, vals.length);
MemContentsSub.ContentsInterface page = pages[pageStart];
if (!page.matches(vals, startOffs, mask)) {
int[] oldValues = page.get(startOffs, vals.length);
page.load(startOffs, vals, mask);
if (page.isClear())
pages[pageStart] = null;
fireBytesChanged(start, PAGE_SIZE - pageStart, oldValues);
}
nextOffs = vals.length;
}
int[] vals = new int[PAGE_SIZE];
int offs = nextOffs;
for (int i = pageStart + 1; i < pageEnd; i++, offs += PAGE_SIZE) {
MemContentsSub.ContentsInterface page = pages[i];
if (page == null) {
boolean allZeroes = true;
for (int j = 0; j < PAGE_SIZE; j++) {
if ((values[offs + j] & mask) != 0) {
allZeroes = false;
break;
}
}
if (!allZeroes) {
page = MemContentsSub.createContents(PAGE_SIZE, width);
pages[i] = page;
}
}
if (page != null) {
System.arraycopy(values, offs, vals, 0, PAGE_SIZE);
if (!page.matches(vals, startOffs, mask)) {
int[] oldValues = page.get(0, PAGE_SIZE);
page.load(0, vals, mask);
if (page.isClear())
pages[i] = null;
fireBytesChanged((long) i << PAGE_SIZE_BITS, PAGE_SIZE,
oldValues);
}
}
}
if (endOffs >= 0) {
ensurePage(pageEnd);
vals = new int[endOffs + 1];
System.arraycopy(values, offs, vals, 0, endOffs + 1);
MemContentsSub.ContentsInterface page = pages[pageEnd];
if (!page.matches(vals, startOffs, mask)) {
int[] oldValues = page.get(0, endOffs + 1);
page.load(0, vals, mask);
if (page.isClear())
pages[pageEnd] = null;
fireBytesChanged((long) pageEnd << PAGE_SIZE_BITS,
endOffs + 1, oldValues);
}
}
}
}
public void setDimensions(int addrBits, int width) {
if (addrBits == this.addrBits && width == this.width)
return;
this.addrBits = addrBits;
this.width = width;
this.mask = width == 32 ? 0xffffffff : ((1 << width) - 1);
MemContentsSub.ContentsInterface[] oldPages = pages;
int pageCount;
int pageLength;
if (addrBits < PAGE_SIZE_BITS) {
pageCount = 1;
pageLength = 1 << addrBits;
} else {
pageCount = 1 << (addrBits - PAGE_SIZE_BITS);
pageLength = PAGE_SIZE;
}
pages = new MemContentsSub.ContentsInterface[pageCount];
if (oldPages != null) {
int n = Math.min(oldPages.length, pages.length);
for (int i = 0; i < n; i++) {
if (oldPages[i] != null) {
pages[i] = MemContentsSub.createContents(pageLength, width);
int m = Math.max(oldPages[i].getLength(), pageLength);
for (int j = 0; j < m; j++) {
pages[i].set(j, oldPages[i].get(j));
}
}
}
}
if (pageCount == 0 && pages[0] == null) {
pages[0] = MemContentsSub.createContents(pageLength, width);
}
fireMetainfoChanged();
}
}