CRTC refactoring

This commit is contained in:
Seth Morabito committed 2014-08-14 10:53:48 -07:00
1 parent e157217f50
commit eeb246ebc2
3 files changed
+101 -32

No files matched your search

@@ -65,6 +65,11 @@ public class Crtc extends Device {
private int currentRegister = 0; private int currentRegister = 0;
// Status bits
private boolean rowColumnAddressing = false;
private boolean displayEnableSkew = false;
private boolean cursorSkew = false;
private Memory memory; private Memory memory;
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException { public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
@@ -118,8 +123,9 @@ public class Crtc extends Device {
return null; return null;
} }
public int[] getDmaAccess() { public int getCharAtAddress(int address) throws MemoryAccessException {
return memory.getDmaAccess(); // TODO: Row/Column addressing
return memory.read(address);
} }
public int getHorizontalDisplayed() { public int getHorizontalDisplayed() {
@@ -162,6 +168,18 @@ public class Crtc extends Device {
return pageSize; return pageSize;
} }
public boolean getRowColumnAddressing() {
return rowColumnAddressing;
}
public boolean getDisplayEnableSkew() {
return displayEnableSkew;
}
public boolean getCursorSkew() {
return cursorSkew;
}
private void setCurrentRegister(int registerNumber) { private void setCurrentRegister(int registerNumber) {
this.currentRegister = registerNumber; this.currentRegister = registerNumber;
} }
@@ -180,7 +198,9 @@ public class Crtc extends Device {
pageSize = horizontalDisplayed * verticalDisplayed; pageSize = horizontalDisplayed * verticalDisplayed;
break; break;
case MODE_CONTROL: case MODE_CONTROL:
// TODO: Implement multiple addressing modes and cursor skew. rowColumnAddressing = (data & 0x04) != 0;
displayEnableSkew = (data & 0x10) != 0;
cursorSkew = (data & 0x20) != 0;
break; break;
case SCAN_LINE: case SCAN_LINE:
scanLinesPerRow = data; scanLinesPerRow = data;
@@ -25,6 +25,7 @@ package com.loomcom.symon.ui;
import com.loomcom.symon.devices.Crtc; import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.DeviceChangeListener; import com.loomcom.symon.devices.DeviceChangeListener;
import com.loomcom.symon.exceptions.MemoryAccessException;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -36,8 +37,11 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.lang.System.*;
/** /**
* VideoWindow represents a graphics framebuffer backed by a 6545 CRTC. * VideoWindow represents a graphics framebuffer backed by a 6545 CRTC.
* Each time the window's VideoPanel is repainted, the video memory is * Each time the window's VideoPanel is repainted, the video memory is
@@ -65,7 +69,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
private BufferedImage image; private BufferedImage image;
private int[] charRom; private int[] charRom;
private int[] videoRam;
private int horizontalDisplayed; private int horizontalDisplayed;
private int verticalDisplayed; private int verticalDisplayed;
@@ -85,17 +88,21 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
private class VideoPanel extends JPanel { private class VideoPanel extends JPanel {
@Override @Override
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
for (int i = 0; i < crtc.getPageSize(); i++) { try {
int address = crtc.getStartAddress() + i; for (int i = 0; i < crtc.getPageSize(); i++) {
int originX = (i % horizontalDisplayed) * CHAR_WIDTH; int address = crtc.getStartAddress() + i;
int originY = (i / horizontalDisplayed) * scanLinesPerRow; int originX = (i % horizontalDisplayed) * CHAR_WIDTH;
image.getRaster().setPixels(originX, originY, CHAR_WIDTH, scanLinesPerRow, getGlyph(address)); int originY = (i / horizontalDisplayed) * scanLinesPerRow;
image.getRaster().setPixels(originX, originY, CHAR_WIDTH, scanLinesPerRow, getGlyph(address));
}
Graphics2D g2d = (Graphics2D) g;
if (shouldScale) {
g2d.scale(scaleX, scaleY);
}
g2d.drawImage(image, 0, 0, null);
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Memory Access Exception, can't paint video window! " + ex.getMessage());
} }
Graphics2D g2d = (Graphics2D)g;
if (shouldScale) {
g2d.scale(scaleX, scaleY);
}
g2d.drawImage(image, 0, 0, null);
} }
@Override @Override
@@ -132,7 +139,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
this.scheduler = Executors.newSingleThreadScheduledExecutor(); this.scheduler = Executors.newSingleThreadScheduledExecutor();
this.crtc = crtc; this.crtc = crtc;
this.charRom = loadCharRom("/pet.rom"); this.charRom = loadCharRom("/pet.rom");
this.videoRam = crtc.getDmaAccess();
this.scaleX = scaleX; this.scaleX = scaleX;
this.scaleY = scaleY; this.scaleY = scaleY;
this.shouldScale = (scaleX > 1 || scaleY > 1); this.shouldScale = (scaleX > 1 || scaleY > 1);
@@ -158,13 +164,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
} }
public void refreshDisplay() {
// TODO: Verify whether this is necessary. Does `repaint()' do anything if the window is not visible?
if (isVisible()) {
repaint();
}
}
/** /**
* Called by the CRTC on state change. * Called by the CRTC on state change.
*/ */
@@ -239,15 +238,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
* @param address The address of the character being requested. * @param address The address of the character being requested.
* @return An array of integers representing the pixel data. * @return An array of integers representing the pixel data.
*/ */
private int[] getGlyph(int address) { private int[] getGlyph(int address) throws MemoryAccessException {
int chr = videoRam[address]; int chr = crtc.getCharAtAddress(address);
int romOffset = (chr & 0xff) * (CHAR_HEIGHT * CHAR_WIDTH); int romOffset = (chr & 0xff) * (CHAR_HEIGHT * CHAR_WIDTH);
int[] glyph = new int[CHAR_WIDTH * scanLinesPerRow]; int[] glyph = new int[CHAR_WIDTH * scanLinesPerRow];
// Populate the character // Populate the character
for (int i = 0; i < (CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow)); i++) { arraycopy(charRom, romOffset, glyph, 0, CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow));
glyph[i] = charRom[romOffset + i];
}
// Overlay the cursor // Overlay the cursor
if (!hideCursor && crtc.isCursorEnabled() && crtc.getCursorPosition() == address) { if (!hideCursor && crtc.isCursorEnabled() && crtc.getCursorPosition() == address) {
+57 -5
View File
@@ -19,19 +19,17 @@ import static org.mockito.Mockito.*;
public class CrtcTest { public class CrtcTest {
Crtc crtc; Crtc crtc;
Memory memory;
@Mock @Mock
DeviceChangeListener changeListener; DeviceChangeListener changeListener;
@Mock
Memory memory;
@Before @Before
public void createDevices() throws Exception { public void createDevices() throws Exception {
memory = new Memory(0, 0x7fff);
crtc = new Crtc(0x9000, memory); crtc = new Crtc(0x9000, memory);
crtc.registerListener(changeListener); crtc.registerListener(changeListener);
when(memory.startAddress()).thenReturn(0);
when(memory.endAddress()).thenReturn(0x7fff);
} }
@Test @Test
@@ -340,4 +338,58 @@ public class CrtcTest {
crtc.write(1, 0x80); // Can't position cursor crtc.write(1, 0x80); // Can't position cursor
} }
@Test
public void shouldSetRowColumnAddressing() throws Exception {
assertEquals(false, crtc.getRowColumnAddressing());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x04);
assertEquals(true, crtc.getRowColumnAddressing());
}
@Test
public void shouldSetDisplayEnableSkew() throws Exception {
assertEquals(false, crtc.getDisplayEnableSkew());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x10);
assertEquals(true, crtc.getDisplayEnableSkew());
}
@Test
public void shouldSetCursorSkew() throws Exception {
assertEquals(false, crtc.getCursorSkew());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x20);
assertEquals(true, crtc.getCursorSkew());
}
@Test
public void shouldDoStraightBinaryAddressing() throws Exception {
crtc.write(0, 8);
crtc.write(1, 0); // Select straight binary addressing
// Fill the memory with a repeating pattern
int videoMemoryBase = 0x7000;
int j = 0;
for (int i = 0; i < 2048; i++) {
memory.write(videoMemoryBase + i, j);
if (j == 255) {
j = 0;
} else {
j++;
}
}
// Now verify that straight-binary addressing of the CRTC works
j = 0;
for (int i = 0; i < 2048; i++) {
assertEquals(j, crtc.getCharAtAddress(videoMemoryBase + i));
if (j == 255) {
j = 0;
} else {
j++;
}
}
}
} }