Refactor of CRTC
This commit is contained in:
1 parent
e7e3c77e3f
commit
6dde766f5a
11 files changed
+543
-410
No files matched your search
@@ -2,11 +2,9 @@ package com.loomcom.symon.devices;
|
||||
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
import com.loomcom.symon.ui.CrtPanel;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
@@ -14,60 +12,101 @@ import java.io.IOException;
|
||||
*/
|
||||
public class Crtc extends Device {
|
||||
|
||||
public static final int CHAR_WIDTH = 8;
|
||||
public static final int CHAR_HEIGHT = 8;
|
||||
public static final int SCAN_LINES = 9;
|
||||
public static final int COLUMNS = 40;
|
||||
public static final int ROWS = 25;
|
||||
public static final int SCALE = 2;
|
||||
private static final Logger logger = Logger.getLogger(Crtc.class.getName());
|
||||
|
||||
public static final int REGISTER_SELECT = 0;
|
||||
public static final int REGISTER_WRITE = 1;
|
||||
|
||||
public static String CHAR_ROM_RESOURCE = "/pet.rom";
|
||||
// Memory locations in the CRTC address space
|
||||
public static final int REGISTER_SELECT = 0;
|
||||
public static final int REGISTER_WRITE = 1;
|
||||
|
||||
// Registers
|
||||
public static final int HORIZONTAL_TOTAL = 0;
|
||||
public static final int HORIZONTAL_DISPLAYED = 1;
|
||||
public static final int HORIZONTAL_SYNC_POSITION = 2;
|
||||
public static final int H_V_SYNC_WIDTHS = 3;
|
||||
public static final int VERTICAL_TOTAL = 4;
|
||||
public static final int VERTICAL_TOTAL_ADJUST = 5;
|
||||
public static final int VERTICAL_DISPLAYED = 6;
|
||||
public static final int VERTICAL_SYNC_POSITION = 7;
|
||||
public static final int MODE_CONTROL = 8;
|
||||
public static final int SCAN_LINE = 9;
|
||||
public static final int CURSOR_START = 10;
|
||||
public static final int CURSOR_END = 11;
|
||||
public static final int DISPLAY_START_HIGH = 12;
|
||||
public static final int DISPLAY_START_LOW = 13;
|
||||
public static final int CURSOR_POSITION_HIGH = 14;
|
||||
public static final int CURSOR_POSITION_LOW = 15;
|
||||
public static final int LPEN_HIGH = 16;
|
||||
public static final int LPEN_LOW = 17;
|
||||
|
||||
|
||||
/*
|
||||
* These will determine how the Character ROM is decoded,
|
||||
* and are Character ROM dependent.
|
||||
*/
|
||||
|
||||
// R1 - Horizontal Displayed
|
||||
private int horizontalDisplayed;
|
||||
|
||||
// R6 - Vertical Displayed
|
||||
private int verticalDisplayed;
|
||||
|
||||
// R9 - Scan Lines: Number of scan lines per character, including spacing.
|
||||
private int scanLinesPerRow;
|
||||
|
||||
// R10 - Cursor Start / Cursor Mode
|
||||
private int cursorStartLine;
|
||||
private boolean cursorEnabled;
|
||||
private int cursorBlinkRate;
|
||||
|
||||
// R11 - Cursor End
|
||||
private int cursorStopLine;
|
||||
|
||||
// R12, R13 - Display Start Address: The starting address in the video RAM of the displayed page.
|
||||
private int startAddress;
|
||||
|
||||
// R14, R15 - Cursor Position
|
||||
private int cursorPosition;
|
||||
|
||||
// The size, in bytes, of a displayed page of characters.
|
||||
private int pageSize;
|
||||
|
||||
private CrtPanel crtPanel;
|
||||
private int currentRegister = 0;
|
||||
|
||||
public Crtc(int deviceAddress, Memory memory, int videoRamStartAddress) throws MemoryRangeException, IOException {
|
||||
private Memory memory;
|
||||
|
||||
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
|
||||
super(deviceAddress, 2, "CRTC");
|
||||
this.crtPanel = new CrtPanel(loadCharRom(CHAR_ROM_RESOURCE), memory.getDmaAccess(), COLUMNS, ROWS,
|
||||
CHAR_WIDTH, CHAR_HEIGHT,
|
||||
SCALE, SCALE, videoRamStartAddress);
|
||||
}
|
||||
this.memory = memory;
|
||||
|
||||
private byte[] loadCharRom(String resource) throws IOException {
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
bis = new BufferedInputStream(this.getClass().getResourceAsStream(resource));
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
while (bis.available() > 0) {
|
||||
bos.write(bis.read());
|
||||
}
|
||||
bos.flush();
|
||||
bos.close();
|
||||
return bos.toByteArray();
|
||||
} finally {
|
||||
if (bis != null) {
|
||||
bis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CrtPanel getCrtPanel() {
|
||||
return crtPanel;
|
||||
// Defaults
|
||||
this.horizontalDisplayed = 40;
|
||||
this.verticalDisplayed = 25;
|
||||
this.scanLinesPerRow = 9;
|
||||
this.cursorStartLine = 0;
|
||||
this.cursorStopLine = 8;
|
||||
this.startAddress = 0x7000;
|
||||
this.cursorPosition = 0;
|
||||
this.pageSize = horizontalDisplayed * verticalDisplayed;
|
||||
this.cursorEnabled = true;
|
||||
this.cursorBlinkRate = 500;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int address, int data) throws MemoryAccessException {
|
||||
logger.info("[write] Writing CRTC address=" + address + " data=" + data);
|
||||
switch (address) {
|
||||
case REGISTER_SELECT:
|
||||
setCurrentRegister(data);
|
||||
break;
|
||||
case REGISTER_WRITE:
|
||||
writeRegisterValue(data);
|
||||
break;
|
||||
default:
|
||||
throw new MemoryAccessException("No such address.");
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,15 +126,80 @@ public class Crtc extends Device {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[] getDmaAccess() {
|
||||
return memory.getDmaAccess();
|
||||
}
|
||||
|
||||
private int status() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getHorizontalDisplayed() {
|
||||
return horizontalDisplayed;
|
||||
}
|
||||
|
||||
public int getVerticalDisplayed() {
|
||||
return verticalDisplayed;
|
||||
}
|
||||
|
||||
public int getScanLinesPerRow() {
|
||||
return scanLinesPerRow;
|
||||
}
|
||||
|
||||
public int getCursorStartLine() {
|
||||
return cursorStartLine;
|
||||
}
|
||||
|
||||
public int getCursorStopLine() {
|
||||
return cursorStopLine;
|
||||
}
|
||||
|
||||
public int getCursorBlinkRate() {
|
||||
return cursorBlinkRate;
|
||||
}
|
||||
|
||||
public boolean isCursorEnabled() {
|
||||
return cursorEnabled;
|
||||
}
|
||||
|
||||
public int getStartAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int getCursorPosition() {
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
private void setCurrentRegister(int registerNumber) {
|
||||
this.currentRegister = registerNumber;
|
||||
}
|
||||
|
||||
private void writeRegisterValue(int data) {
|
||||
logger.info("Writing CRTC Register #" + currentRegister + " with value " + String.format("$%02X", data));
|
||||
switch (currentRegister) {
|
||||
case HORIZONTAL_DISPLAYED:
|
||||
horizontalDisplayed = data;
|
||||
pageSize = horizontalDisplayed * verticalDisplayed;
|
||||
break;
|
||||
case VERTICAL_DISPLAYED:
|
||||
verticalDisplayed = data;
|
||||
pageSize = horizontalDisplayed * verticalDisplayed;
|
||||
break;
|
||||
case MODE_CONTROL:
|
||||
// TODO: Implement multiple addressing modes.
|
||||
break;
|
||||
case SCAN_LINE:
|
||||
scanLinesPerRow = data;
|
||||
break;
|
||||
default:
|
||||
logger.info("Ignoring.");
|
||||
break;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,13 @@
|
||||
|
||||
package com.loomcom.symon.devices;
|
||||
|
||||
import com.loomcom.symon.*;
|
||||
import com.loomcom.symon.exceptions.*;
|
||||
import com.loomcom.symon.Bus;
|
||||
import com.loomcom.symon.MemoryRange;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A memory-mapped IO Device.
|
||||
@@ -32,69 +37,93 @@ import com.loomcom.symon.exceptions.*;
|
||||
|
||||
public abstract class Device implements Comparable<Device> {
|
||||
|
||||
/** The memory range for this device. */
|
||||
private MemoryRange memoryRange;
|
||||
/**
|
||||
* The memory range for this device.
|
||||
*/
|
||||
private MemoryRange memoryRange;
|
||||
|
||||
/** The name of the device. */
|
||||
private String name;
|
||||
/**
|
||||
* The name of the device.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/** Reference to the bus where this Device is attached. */
|
||||
private Bus bus;
|
||||
/**
|
||||
* Reference to the bus where this Device is attached.
|
||||
*/
|
||||
private Bus bus;
|
||||
|
||||
public Device(int address, int size, String name)
|
||||
throws MemoryRangeException {
|
||||
this.memoryRange = new MemoryRange(address, address + size - 1);
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* Listeners to notify on update.
|
||||
*/
|
||||
private Set<DeviceChangeListener> deviceChangeListeners;
|
||||
|
||||
public Device(int address, int size) throws MemoryRangeException {
|
||||
this(address, size, null);
|
||||
}
|
||||
|
||||
/* Methods required to be implemented by inheriting classes. */
|
||||
public abstract void write(int address, int data) throws MemoryAccessException;
|
||||
public abstract int read(int address) throws MemoryAccessException;
|
||||
public abstract String toString();
|
||||
|
||||
public void setBus(Bus bus) {
|
||||
this.bus = bus;
|
||||
}
|
||||
|
||||
public Bus getBus() {
|
||||
return this.bus;
|
||||
}
|
||||
|
||||
public MemoryRange getMemoryRange() {
|
||||
return memoryRange;
|
||||
}
|
||||
|
||||
public int endAddress() {
|
||||
return memoryRange.endAddress();
|
||||
}
|
||||
|
||||
public int startAddress() {
|
||||
return memoryRange.startAddress();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two devices. The sort order is defined by the sort
|
||||
* order of the device's memory ranges.
|
||||
*/
|
||||
public int compareTo(Device other) {
|
||||
if (other == null) {
|
||||
throw new NullPointerException("Cannot compare to null.");
|
||||
public Device(int address, int size, String name)
|
||||
throws MemoryRangeException {
|
||||
this.memoryRange = new MemoryRange(address, address + size - 1);
|
||||
this.name = name;
|
||||
this.deviceChangeListeners = new HashSet<DeviceChangeListener>();
|
||||
}
|
||||
if (this == other) {
|
||||
return 0;
|
||||
|
||||
public Device(int address, int size) throws MemoryRangeException {
|
||||
this(address, size, null);
|
||||
}
|
||||
|
||||
/* Methods required to be implemented by inheriting classes. */
|
||||
public abstract void write(int address, int data) throws MemoryAccessException;
|
||||
|
||||
public abstract int read(int address) throws MemoryAccessException;
|
||||
|
||||
public abstract String toString();
|
||||
|
||||
public Bus getBus() {
|
||||
return this.bus;
|
||||
}
|
||||
|
||||
public void setBus(Bus bus) {
|
||||
this.bus = bus;
|
||||
}
|
||||
|
||||
public MemoryRange getMemoryRange() {
|
||||
return memoryRange;
|
||||
}
|
||||
|
||||
public int endAddress() {
|
||||
return memoryRange.endAddress();
|
||||
}
|
||||
|
||||
public int startAddress() {
|
||||
return memoryRange.startAddress();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void registerListener(DeviceChangeListener listener) {
|
||||
deviceChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
public void notifyListeners() {
|
||||
for (DeviceChangeListener l : deviceChangeListeners) {
|
||||
l.deviceStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two devices. The sort order is defined by the sort
|
||||
* order of the device's memory ranges.
|
||||
*/
|
||||
public int compareTo(Device other) {
|
||||
if (other == null) {
|
||||
throw new NullPointerException("Cannot compare to null.");
|
||||
}
|
||||
if (this == other) {
|
||||
return 0;
|
||||
}
|
||||
return getMemoryRange().compareTo(other.getMemoryRange());
|
||||
}
|
||||
return getMemoryRange().compareTo(other.getMemoryRange());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.loomcom.symon.devices;
|
||||
|
||||
public interface DeviceChangeListener {
|
||||
public void deviceStateChanged();
|
||||
}
|
||||
Reference in new issue
Block a user