mirror of
https://github.com/badvision/jace.git
synced 2024-06-08 09:29:32 +00:00
0ccb63558f
The video rendering was re-written to use writableImages and is displaying (something) but keyboard input and configurations are broken so nothing much happens after the inital boot. Basically the underlying part to make this show up in JavaFX is starting to take shape.
747 lines
28 KiB
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.util.logging.Logger;
|
|
import javafx.scene.image.PixelWriter;
|
|
import javafx.scene.image.WritableImage;
|
|
import javafx.scene.paint.Color;
|
|
|
|
/**
|
|
* 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(Computer computer) {
|
|
super(computer);
|
|
hiresPage1 = new VideoWriter() {
|
|
@Override
|
|
public int getYOffset(int y) {
|
|
return (hiresOffset[y] + 0x02000);
|
|
}
|
|
|
|
@Override
|
|
public void displayByte(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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(WritableImage 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.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset);
|
|
int b2 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset);
|
|
int b3 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1);
|
|
int b4 = ((RAM128k) computer.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(WritableImage 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.getMemory()).getMainMemory().readByte(rowAddress + xOffset);
|
|
int b2 = 0x0ff & ((RAM128k) computer.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(WritableImage screen, int xOffset, int y, int rowAddress) {
|
|
int c1 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
|
|
if ((y & 7) < 4) {
|
|
c1 &= 15;
|
|
} else {
|
|
c1 >>= 4;
|
|
}
|
|
Color color = Palette.color[c1];
|
|
// Unrolled loop, faster
|
|
PixelWriter writer = screen.getPixelWriter();
|
|
int x = xOffset * 7;
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
}
|
|
|
|
private void displayDoubleLores(WritableImage screen, int xOffset, int y, int rowAddress) {
|
|
int c1 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset) & 0x0FF;
|
|
int c2 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
|
|
if ((y & 7) < 4) {
|
|
c1 &= 15;
|
|
c2 &= 15;
|
|
} else {
|
|
c1 >>= 4;
|
|
c2 >>= 4;
|
|
}
|
|
PixelWriter writer = screen.getPixelWriter();
|
|
// int yOffset = xyOffset[y][times14[xOffset]];
|
|
Color color = Palette.color[c1];
|
|
// Unrolled loop, faster
|
|
int x = xOffset * 7;
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
color = Palette.color[c2];
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
}
|
|
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(WritableImage 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.getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1);
|
|
int c1 = getFontChar(((RAM128k) computer.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(WritableImage 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.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset));
|
|
int c2 = getFontChar(((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset));
|
|
int c3 = getFontChar(((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1));
|
|
int c4 = getFontChar(((RAM128k) computer.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(WritableImage 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(WritableImage screen, int xOffset, int y, int dhgrWord) {
|
|
//Graphics2D g = (Graphics2D) screen.getGraphics();
|
|
int x = xOffset * 7;
|
|
PixelWriter writer = screen.getPixelWriter();
|
|
try {
|
|
for (int i = 0; i < 7; i++) {
|
|
Color color = Palette.color[flipNybble[dhgrWord & 15]];
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
writer.setColor(x++, y, color);
|
|
dhgrWord >>= 4;
|
|
}
|
|
} catch (ArrayIndexOutOfBoundsException ex) {
|
|
Logger.getLogger(getClass().getName()).warning("Went out of bounds in video display");
|
|
}
|
|
}
|
|
static final Color BLACK = Color.BLACK;
|
|
static final Color WHITE = Color.WHITE;
|
|
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(WritableImage 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!
|
|
// 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;
|
|
|
|
int x = xOffset * 7;
|
|
PixelWriter writer = screen.getPixelWriter();
|
|
for (int i = 0; i < 28; i++) {
|
|
// yOffset++ is used instead of yOffset+i, because it is faster
|
|
writer.setColor(x++, y, (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.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.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(WritableImage screen, int y, boolean isDirty) {
|
|
// Do nothing
|
|
}
|
|
}
|