package org.jpc.emulator.motherboard; import java.io.*; import java.util.logging.*; import org.jpc.emulator.*; import org.jpc.emulator.memory.*; public abstract class Bios extends AbstractHardwareComponent { private byte[] imageData; private boolean loaded; private final Logger biosOutput; private final StringBuilder biosOutputBuffer = new StringBuilder(); private static final int BIOS_ROM_SPACE_SIZE = 2*1024*1024; public Bios(byte[] image) { this(image, "byte_array"); } public Bios(String image) throws IOException { this(getBiosData(image), image.replace('/', '.')); } private Bios(byte[] image, String identity) { imageData = new byte[image.length]; System.arraycopy(image, 0, imageData, 0, image.length); loaded = false; biosOutput = Logger.getLogger(Bios.class.getName() + ".output" + identity); } public void saveState(DataOutput output) throws IOException { output.writeInt(imageData.length); output.write(imageData); } public void loadState(DataInput input) throws IOException { loaded = false; imageData = new byte[input.readInt()]; input.readFully(imageData); } private void load(PhysicalAddressSpace addressSpace) { if (this instanceof SystemBIOS) { if ((imageData.length & (~(1 << (31 -Integer.numberOfLeadingZeros(imageData.length))))) != 0) throw new IllegalStateException("BIOS image size is not a power of 2: "+imageData.length); int lowAddress = 0x100000 - AddressSpace.BLOCK_SIZE; int endAddress = -AddressSpace.BLOCK_SIZE; int imageOffset = imageData.length - AddressSpace.BLOCK_SIZE; for (; endAddress >= -imageData.length; endAddress -= AddressSpace.BLOCK_SIZE, imageOffset -= AddressSpace.BLOCK_SIZE, lowAddress -= AddressSpace.BLOCK_SIZE) { EPROMMemory eprom = new EPROMMemory(AddressSpace.BLOCK_SIZE, 0, imageData, imageOffset, AddressSpace.BLOCK_SIZE, addressSpace.getCodeBlockManager()); addressSpace.mapMemory(endAddress, eprom); // now map the shadow copy from E0000 to 0x100000 (up to last 128K of image only) if ((lowAddress >= 0xE0000) && (0x100000 - lowAddress <= imageData.length)) { ShadowEPROMMemory shadow = new ShadowEPROMMemory(AddressSpace.BLOCK_SIZE, eprom, addressSpace.getCodeBlockManager()); addressSpace.mapMemory(lowAddress, shadow); } } } else { // other ROMs may not be a power of 2 in size int loadAddress = loadAddress(); int nextBlockStart = (loadAddress & AddressSpace.INDEX_MASK) + AddressSpace.BLOCK_SIZE; EPROMMemory ep = new EPROMMemory(AddressSpace.BLOCK_SIZE, loadAddress & AddressSpace.BLOCK_MASK, imageData, 0, nextBlockStart - loadAddress, addressSpace.getCodeBlockManager()); ShadowEPROMMemory shadow = new ShadowEPROMMemory(AddressSpace.BLOCK_SIZE, ep, addressSpace.getCodeBlockManager()); addressSpace.mapMemory(loadAddress & AddressSpace.INDEX_MASK, shadow); int imageOffset = nextBlockStart - loadAddress; int epromOffset = nextBlockStart; while ((imageOffset + AddressSpace.BLOCK_SIZE) <= imageData.length) { ep = new EPROMMemory(imageData, imageOffset, AddressSpace.BLOCK_SIZE, addressSpace.getCodeBlockManager()); shadow = new ShadowEPROMMemory(AddressSpace.BLOCK_SIZE, ep, addressSpace.getCodeBlockManager()); addressSpace.mapMemory(epromOffset, shadow); epromOffset += AddressSpace.BLOCK_SIZE; imageOffset += AddressSpace.BLOCK_SIZE; } if (imageOffset < imageData.length) { ep = new EPROMMemory(AddressSpace.BLOCK_SIZE, 0, imageData, imageOffset, imageData.length - imageOffset, addressSpace.getCodeBlockManager()); shadow = new ShadowEPROMMemory(AddressSpace.BLOCK_SIZE, ep, addressSpace.getCodeBlockManager()); addressSpace.mapMemory(epromOffset, shadow); } } } protected abstract int loadAddress(); public boolean updated() { return loaded; } public void updateComponent(HardwareComponent component) { if ((component instanceof PhysicalAddressSpace) && component.updated()) { this.load((PhysicalAddressSpace) component); loaded = true; } } public boolean initialised() { return loaded; } public void acceptComponent(HardwareComponent component) { if ((component instanceof PhysicalAddressSpace) && component.initialised()) { this.load((PhysicalAddressSpace) component); loaded = true; } } public void reset() { loaded = false; } public int length() { return imageData.length; } protected void print(String data) { synchronized (biosOutputBuffer) { int newline; while ((newline = data.indexOf('\n')) >= 0) { biosOutputBuffer.append(data.substring(0, newline)); biosOutput.log(Level.INFO, biosOutputBuffer.toString()); biosOutputBuffer.delete(0, biosOutputBuffer.length()); data = data.substring(newline + 1); } biosOutputBuffer.append(data); } } private static final byte[] getBiosData(String image) throws IOException { InputStream in = Bios.class.getResourceAsStream(image); if (in == null) { throw new IOException("resource not found: " + image); } try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); while (true) { int ch = in.read(); if (ch < 0) { break; } bout.write((byte) ch); } return bout.toByteArray(); } finally { try { in.close(); } catch (IOException e) { } } } }