jace/src/main/java/jace/apple2e/VideoDHGR.java

747 lines
28 KiB
Java

/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.apple2e;
import jace.core.Computer;
import jace.core.Font;
import jace.core.Palette;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.Video;
import jace.core.VideoWriter;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.util.logging.Logger;
/**
* This is the primary video rendering class, which provides all necessary video
* writers for every display mode as well as managing the display mode (via
* configureVideoMode). The quality of the color rendering is sub-par compared
* to VideoNTSC.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class VideoDHGR extends Video {
// Reorder bits 3,2,1,0 -> 0,3,2,1
// Fixes double-hires color palette
public final static int flipNybble[] = {
0, 2, 4, 6,
8, 10, 12, 14,
1, 3, 5, 7,
9, 11, 13, 15
};
private static final boolean USE_GS_MOUSETEXT = false;
private VideoWriter textPage1;
private VideoWriter textPage2;
private VideoWriter loresPage1;
private VideoWriter loresPage2;
private VideoWriter hiresPage1;
private VideoWriter hiresPage2;
// Special 80-column modes
private VideoWriter text80Page1;
private VideoWriter text80Page2;
private VideoWriter dloresPage1;
private VideoWriter dloresPage2;
private VideoWriter dhiresPage1;
private VideoWriter dhiresPage2;
// Mixed mode
private VideoWriter mixed;
private VideoWriter currentGraphicsWriter = null;
private VideoWriter currentTextWriter = null;
/**
* Creates a new instance of VideoDHGR
*/
public VideoDHGR() {
hiresPage1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (hiresOffset[y] + 0x02000);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayHires(screen, xOffset, y, yGraphicsOffset + 0x02000);
}
};
hiresPage2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (hiresOffset[y] + 0x04000);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayHires(screen, xOffset, y, yGraphicsOffset + 0x04000);
}
};
dhiresPage1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (hiresOffset[y] + 0x02000);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayDoubleHires(screen, xOffset, y, yGraphicsOffset + 0x02000);
}
@Override
public VideoWriter actualWriter() {
return hiresPage1;
}
};
dhiresPage2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (hiresOffset[y] + 0x04000);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayDoubleHires(screen, xOffset, y, yGraphicsOffset + 0x04000);
}
@Override
public VideoWriter actualWriter() {
return hiresPage2;
}
};
textPage1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0400);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayText(screen, xOffset, y, yTextOffset + 0x0400);
}
};
textPage2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0800);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayText(screen, xOffset, y, yTextOffset + 0x0800);
}
};
text80Page1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0400);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayText80(screen, xOffset, y, yTextOffset + 0x0400);
}
@Override
public VideoWriter actualWriter() {
return textPage1;
}
};
text80Page2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0800);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayText80(screen, xOffset, y, yTextOffset + 0x0800);
}
@Override
public VideoWriter actualWriter() {
return textPage2;
}
};
loresPage1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0400);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayLores(screen, xOffset, y, yTextOffset + 0x0400);
}
@Override
public VideoWriter actualWriter() {
return textPage1;
}
};
loresPage2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0800);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayLores(screen, xOffset, y, yTextOffset + 0x0800);
}
@Override
public VideoWriter actualWriter() {
return textPage2;
}
};
dloresPage1 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0400);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayDoubleLores(screen, xOffset, y, yTextOffset + 0x0400);
}
@Override
public VideoWriter actualWriter() {
return textPage1;
}
};
dloresPage2 = new VideoWriter() {
@Override
public int getYOffset(int y) {
return (textOffset[y] + 0x0800);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayDoubleLores(screen, xOffset, y, yTextOffset + 0x0800);
}
@Override
public VideoWriter actualWriter() {
return textPage2;
}
};
mixed = new VideoWriter() {
@Override
public int getYOffset(int y) {
return actualWriter().getYOffset(y);
}
@Override
public void displayByte(BufferedImage screen, int xOffset, int y, int yTextOffset, int yGraphicsOffset) {
displayMixed(screen, xOffset, y, yTextOffset, yGraphicsOffset);
}
@Override
public void markDirty(int y) {
actualWriter().actualWriter().markDirty(y);
}
@Override
public void clearDirty(int y) {
actualWriter().actualWriter().clearDirty(y);
}
@Override
public boolean isRowDirty(int y) {
return actualWriter().actualWriter().isRowDirty(y);
}
@Override
public VideoWriter actualWriter() {
if (y < 160) {
return currentGraphicsWriter;
}
return currentTextWriter;
}
@Override
public boolean isMixed() {
return true;
}
};
registerDirtyFlagChecks();
}
// color burst per byte (chat mauve compatibility)
boolean[] useColor = new boolean[80];
protected void displayDoubleHires(BufferedImage screen, int xOffset, int y, int rowAddress) {
// Skip odd columns since this does two at once
if ((xOffset & 0x01) == 1) {
return;
}
int b1 = ((RAM128k) Computer.getComputer().getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset);
int b2 = ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset);
int b3 = ((RAM128k) Computer.getComputer().getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1);
int b4 = ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1);
int useColOffset = xOffset << 1;
// This shouldn't be necessary but prevents an index bounds exception when graphics modes are flipped (Race condition?)
if (useColOffset >= 77) {
useColOffset = 76;
}
useColor[useColOffset] = (b1 & 0x80) != 0;
useColor[useColOffset + 1] = (b2 & 0x80) != 0;
useColor[useColOffset + 2] = (b3 & 0x80) != 0;
useColor[useColOffset + 3] = (b4 & 0x80) != 0;
int dhgrWord = 0x07f & b1;
dhgrWord |= (0x07f & b2) << 7;
dhgrWord |= (0x07f & b3) << 14;
dhgrWord |= (0x07f & b4) << 21;
showDhgr(screen, times14[xOffset], y, dhgrWord);
}
boolean extraHalfBit = false;
protected void displayHires(BufferedImage screen, int xOffset, int y, int rowAddress) {
// Skip odd columns since this does two at once
if ((xOffset & 0x01) == 1) {
return;
}
int b1 = 0x0ff & ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset);
int b2 = 0x0ff & ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1);
int dhgrWord = hgrToDhgr[(extraHalfBit && xOffset > 0) ? b1 | 0x0100 : b1][b2];
extraHalfBit = (dhgrWord & 0x10000000) != 0;
showDhgr(screen, times14[xOffset], y, dhgrWord & 0xfffffff);
// If you want monochrome, use this instead...
// showBW(screen, times14[xOffset], y, dhgrWord);
}
// Take two consecutive bytes and double them, taking hi-bit into account
// This should yield a 28-bit word of 7 color dhgr pixels
// This looks like crap on text...
static final int[][] hgrToDhgr;
// Take two consecutive bytes and double them, disregarding hi-bit
// Useful for text mode
static final int[][] hgrToDhgrBW;
static final int[] times14;
static final int[] flipBits;
static {
// complete reverse of 8 bits
flipBits = new int[256];
for (int i = 0; i < 256; i++) {
flipBits[i] = (((i * 0x0802 & 0x22110) | (i * 0x8020 & 0x88440)) * 0x10101 >> 16) & 0x0ff;
}
times14 = new int[40];
for (int i = 0; i < 40; i++) {
times14[i] = i * 14;
}
hgrToDhgr = new int[512][256];
hgrToDhgrBW = new int[256][256];
for (int bb1 = 0; bb1 < 512; bb1++) {
for (int bb2 = 0; bb2 < 256; bb2++) {
int value = ((bb1 & 0x0181) >= 0x0101) ? 1 : 0;
int b1 = byteDoubler((byte) (bb1 & 0x07f));
if ((bb1 & 0x080) != 0) {
b1 <<= 1;
}
int b2 = byteDoubler((byte) (bb2 & 0x07f));
if ((bb2 & 0x080) != 0) {
b2 <<= 1;
}
if ((bb1 & 0x040) == 0x040 && (bb2 & 1) != 0) {
b2 |= 1;
}
value |= b1 | (b2 << 14);
if ((bb2 & 0x040) != 0) {
value |= 0x10000000;
}
hgrToDhgr[bb1][bb2] = value;
hgrToDhgrBW[bb1 & 0x0ff][bb2] =
byteDoubler((byte) bb1) | (byteDoubler((byte) bb2) << 14);
}
}
}
protected void displayLores(BufferedImage screen, int xOffset, int y, int rowAddress) {
int c1 = ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
if ((y & 7) < 4) {
c1 &= 15;
} else {
c1 >>= 4;
}
DataBuffer b = screen.getRaster().getDataBuffer();
int yOffset = xyOffset[y][times14[xOffset]];
int color = Palette.color[c1].getRGB();
// Unrolled loop, faster
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
}
private void displayDoubleLores(BufferedImage screen, int xOffset, int y, int rowAddress) {
int c1 = ((RAM128k) Computer.getComputer().getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset) & 0x0FF;
int c2 = ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
if ((y & 7) < 4) {
c1 &= 15;
c2 &= 15;
} else {
c1 >>= 4;
c2 >>= 4;
}
DataBuffer b = screen.getRaster().getDataBuffer();
int yOffset = xyOffset[y][times14[xOffset]];
int color = Palette.color[c1].getRGB();
int color2 = Palette.color[c2].getRGB();
// Unrolled loop, faster
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
b.setElem(yOffset++, color2);
}
boolean flashInverse = false;
int flashTimer = 0;
int FLASH_SPEED = 16; // UTAIIe:8-13,P7 - FLASH toggles every 16 scans
int[] currentCharMap = CHAR_MAP1;
static final int[] CHAR_MAP1;
static final int[] CHAR_MAP2;
static final int[] CHAR_MAP3;
static {
// Generate screen text lookup maps ahead of time
// ALTCHR clear
// 00-3F - Inverse characters (uppercase only) "@P 0"
// 40-7F - Flashing characters (uppercase only) "@P 0"
// 80-BF - Normal characters (uppercase only) "@P 0"
// C0-DF - Normal characters (repeat 80-9F) "@P"
// E0-FF - Normal characters (lowercase) "`p"
// ALTCHR set
// 00-3f - Inverse characters (uppercase only) "@P 0"
// 40-5f - Mousetext (//gs alts are at 0x46 and 0x47, swap with 0x11 and 0x12 for //e and //c)
// 60-7f - Inverse characters (lowercase only)
// 80-BF - Normal characters (uppercase only)
// C0-DF - Normal characters (repeat 80-9F)
// E0-FF - Normal characters (lowercase)
// MAP1: Normal map, flash inverse = false
CHAR_MAP1 = new int[256];
// MAP2: Normal map, flash inverse = true
CHAR_MAP2 = new int[256];
// MAP3: Alt map, mousetext mode
CHAR_MAP3 = new int[256];
for (int b = 0; b < 256; b++) {
int mod = b % 0x020;
// Inverse
if (b < 0x020) {
CHAR_MAP1[b] = mod + 0x0c0;
CHAR_MAP2[b] = mod + 0x0c0;
CHAR_MAP3[b] = mod + 0x0c0;
} else if (b < 0x040) {
CHAR_MAP1[b] = mod + 0x0a0;
CHAR_MAP2[b] = mod + 0x0a0;
CHAR_MAP3[b] = mod + 0x0a0;
} else if (b < 0x060) {
// Flash/Mouse
CHAR_MAP1[b] = mod + 0x0c0;
CHAR_MAP2[b] = mod + 0x040;
if (!USE_GS_MOUSETEXT && mod == 6) {
CHAR_MAP3[b] = 0x011;
} else if (!USE_GS_MOUSETEXT && mod == 7) {
CHAR_MAP3[b] = 0x012;
} else {
CHAR_MAP3[b] = mod + 0x080;
}
} else if (b < 0x080) {
// Flash/Inverse lowercase
CHAR_MAP1[b] = mod + 0x0a0;
CHAR_MAP2[b] = mod + 0x020;
CHAR_MAP3[b] = mod + 0x0e0;
} else if (b < 0x0a0) {
// Normal uppercase
CHAR_MAP1[b] = mod + 0x040;
CHAR_MAP2[b] = mod + 0x040;
CHAR_MAP3[b] = mod + 0x040;
} else if (b < 0x0c0) {
// Normal uppercase
CHAR_MAP1[b] = mod + 0x020;
CHAR_MAP2[b] = mod + 0x020;
CHAR_MAP3[b] = mod + 0x020;
} else if (b < 0x0e0) {
// Normal uppercase (repeat)
CHAR_MAP1[b] = mod + 0x040;
CHAR_MAP2[b] = mod + 0x040;
CHAR_MAP3[b] = mod + 0x040;
} else {
// Normal lowercase
CHAR_MAP1[b] = mod + 0x060;
CHAR_MAP2[b] = mod + 0x060;
CHAR_MAP3[b] = mod + 0x060;
}
}
}
@Override
public void vblankStart() {
// ALTCHR set only affects character mapping and disables FLASH.
if (SoftSwitches.ALTCH.isOn()) {
currentCharMap = CHAR_MAP3;
} else {
flashTimer--;
if (flashTimer <= 0) {
markFlashDirtyBits();
flashTimer = FLASH_SPEED;
flashInverse = !flashInverse;
if (flashInverse) {
currentCharMap = CHAR_MAP2;
} else {
currentCharMap = CHAR_MAP1;
}
}
}
super.vblankStart();
}
@Override
public void vblankEnd() {
}
private int getFontChar(byte b) {
return currentCharMap[b & 0x0ff];
}
protected void displayText(BufferedImage screen, int xOffset, int y, int rowAddress) {
// Skip odd columns since this does two at once
if ((xOffset & 0x01) == 1) {
return;
}
int yOffset = y & 7;
byte byte2 = ((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1);
int c1 = getFontChar(((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset));
int c2 = getFontChar(byte2);
int b1 = Font.getByte(c1, yOffset);
int b2 = Font.getByte(c2, yOffset);
// Why is this getting inversed now? Bug in hgrToDhgrBW?
// Nick says: are you getting confused because the //e video ROM is inverted? (1=black)
int out = hgrToDhgrBW[b1][b2];
showBW(screen, times14[xOffset], y, out);
}
protected void displayText80(BufferedImage screen, int xOffset, int y, int rowAddress) {
// Skip odd columns since this does two at once
if ((xOffset & 0x01) == 1) {
return;
}
int yOffset = y & 7;
int c1 = getFontChar(((RAM128k) Computer.getComputer().getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset));
int c2 = getFontChar(((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset));
int c3 = getFontChar(((RAM128k) Computer.getComputer().getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1));
int c4 = getFontChar(((RAM128k) Computer.getComputer().getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1));
int bits = Font.getByte(c1, yOffset) | (Font.getByte(c2, yOffset) << 7)
| (Font.getByte(c3, yOffset) << 14) | (Font.getByte(c4, yOffset) << 21);
showBW(screen, times14[xOffset], y, bits);
}
private void displayMixed(BufferedImage screen, int xOffset, int y, int textOffset, int graphicsOffset) {
mixed.actualWriter().displayByte(screen, xOffset, y, textOffset, graphicsOffset);
}
protected boolean hiresMode = false;
public boolean dhgrMode = false;
@Override
public void configureVideoMode() {
boolean page2 = SoftSwitches.PAGE2.isOn() && SoftSwitches._80STORE.isOff();
dhgrMode = SoftSwitches._80COL.getState() && SoftSwitches.DHIRES.getState() && SoftSwitches.HIRES.getState();
currentTextWriter =
SoftSwitches._80COL.getState()
? page2
? text80Page2 : text80Page1
: page2
? textPage2 : textPage1;
currentGraphicsWriter =
SoftSwitches._80COL.getState() && SoftSwitches.DHIRES.getState()
? SoftSwitches.HIRES.getState()
? page2
? dhiresPage2 : dhiresPage1
: page2
? dloresPage2 : dloresPage1
: SoftSwitches.HIRES.getState()
? page2
? hiresPage2 : hiresPage1
: page2
? loresPage2 : loresPage1;
setCurrentWriter(
SoftSwitches.TEXT.getState() ? currentTextWriter
: SoftSwitches.MIXED.getState() ? mixed
: currentGraphicsWriter);
hiresMode = !SoftSwitches.DHIRES.getState();
}
protected void showDhgr(BufferedImage screen, int xOffset, int y, int dhgrWord) {
//Graphics2D g = (Graphics2D) screen.getGraphics();
DataBuffer b = screen.getRaster().getDataBuffer();
int yOffset = xyOffset[y][xOffset];
try {
for (int i = 0; i < 7; i++) {
int color = Palette.color[flipNybble[dhgrWord & 15]].getRGB();
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
b.setElem(yOffset++, color);
dhgrWord >>= 4;
}
} catch (ArrayIndexOutOfBoundsException ex) {
Logger.getLogger(getClass().getName()).warning("Went out of bounds in video display");
}
}
static final int BLACK = Color.BLACK.getRGB();
static final int WHITE = Color.WHITE.getRGB();
static final int[][] xyOffset;
static {
xyOffset = new int[192][560];
for (int y = 0; y < 192; y++) {
for (int x = 0; x < 560; x++) {
xyOffset[y][x] = y * 560 + x;
}
}
}
protected void showBW(BufferedImage screen, int xOffset, int y, int dhgrWord) {
int color = 0;
// Using the data buffer directly is about 15 times faster than setRGB
// This is because setRGB does extra (useless) color model logic
// For that matter even Graphics.drawLine is faster than setRGB!
DataBuffer b = screen.getRaster().getDataBuffer();
// This is equivilant to y*560 but is 5% faster
// Also, adding xOffset now makes it additionally 5% faster
//int yOffset = ((y << 4) + (y << 5) + (y << 9))+xOffset;
//is this lookup faster?
int yOffset = xyOffset[y][xOffset];
for (int i = 0; i < 28; i++) {
// yOffset++ is used instead of yOffset+i, because it is faster
b.setElem(yOffset++, (dhgrWord & 1) == 1 ? WHITE : BLACK);
dhgrWord >>= 1;
}
}
/**
*
*/
@Override
public void doPostDraw() {
}
/**
*
* @return
*/
@Override
protected String getDeviceName() {
return "DHGR-Capable Video";
}
private void markFlashDirtyBits() {
// TODO: Be smarter about detecting where flash is used... one day...
for (int row = 0; row < 192; row++) {
currentTextWriter.markDirty(row);
}
}
private void registerDirtyFlagChecks() {
((RAM128k) Computer.getComputer().getMemory()).addListener(new RAMListener(RAMEvent.TYPE.WRITE, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(0x0400);
setScopeEnd(0x0bff);
}
@Override
protected void doEvent(RAMEvent e) {
int row = textRowLookup[e.getAddress() & 0x03ff];
// int row = identifyTextRow(e.getAddress() & 0x03ff);
if (row > 23) {
return;
}
VideoWriter tmark = (e.getAddress() < 0x0800) ? textPage1 : textPage2;
row <<= 3;
int yy = row + 8;
for (int y = row; y < yy; y++) {
tmark.markDirty(y);
}
}
});
((RAM128k) Computer.getComputer().getMemory()).addListener(new RAMListener(RAMEvent.TYPE.WRITE, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(0x2000);
setScopeEnd(0x5fff);
}
@Override
protected void doEvent(RAMEvent e) {
int row = hiresRowLookup[e.getAddress() & 0x01fff];
// int row = identifyHiresRow(e.getAddress() & 0x03fff);
if (row < 0 || row >= 192) {
return;
}
VideoWriter mark = (e.getAddress() < 0x04000) ? hiresPage1 : hiresPage2;
mark.markDirty(row);
}
});
}
@Override
public void reconfigure() {
// Do nothing (for now)
}
@Override
public void attach() {
// Do nothing
}
@Override
public void detach() {
// Do nothing
}
@Override
public void hblankStart(BufferedImage screen, int y, boolean isDirty) {
// Do nothing
}
}