1047 lines
43 KiB
Java
1047 lines
43 KiB
Java
/*
|
|
* AppleIIGo
|
|
* Display processing
|
|
* (C) 2006 by Marc S. Ressl (ressl@lonetree.com)
|
|
* Released under the GPL
|
|
*/
|
|
|
|
package vavi.apps.appleii;
|
|
|
|
|
|
/**
|
|
* AppleDisplay class<p>
|
|
* Refreshes the display
|
|
*/
|
|
public class AppleDisplay implements Runnable {
|
|
// Instances of other classes
|
|
private EmAppleII apple;
|
|
|
|
// Configuration variables
|
|
public static final int COLORMODE_GREEN = 0;
|
|
public static final int COLORMODE_COLOR = 1;
|
|
|
|
private int colorMode;
|
|
|
|
// Refresh
|
|
private int refreshRate;
|
|
private long refreshInterval;
|
|
private long refreshDelayCumulative;
|
|
private long refreshDelayPerSecond;
|
|
private long refreshCycle;
|
|
|
|
private boolean isPrecalcRequested = true;
|
|
private boolean isRefreshRequested = true;
|
|
|
|
// Graphics interface variables
|
|
private boolean[] graphicsDirty = new boolean[0x6000 >> 7];
|
|
private int graphicsMode;
|
|
|
|
// Display
|
|
private static final int DISPLAY_CHAR_SIZE_X = 7;
|
|
private static final int DISPLAY_CHAR_SIZE_Y = 8;
|
|
private static final int DISPLAY_CHAR_COUNT_X = 80;
|
|
private static final int DISPLAY_CHAR_COUNT_Y = 24;
|
|
public static final int DISPLAY_SIZE_X = DISPLAY_CHAR_COUNT_X * DISPLAY_CHAR_SIZE_X;
|
|
public static final int DISPLAY_SIZE_Y = DISPLAY_CHAR_COUNT_Y * DISPLAY_CHAR_SIZE_Y;
|
|
|
|
// Display composition
|
|
private int[] displayImageBuffer;
|
|
|
|
public int[] getDisplayImageBuffer() {
|
|
return displayImageBuffer;
|
|
}
|
|
|
|
// Display scale
|
|
private float displayScale;
|
|
|
|
// Display palette
|
|
private int[] displayPalette;
|
|
private static final int[] displayPaletteGreen = {
|
|
0xff000000, 0xff0e470e, 0xff041204, 0xff166e16,
|
|
0xff0f4a0f, 0xff115411, 0xff0c3b0c, 0xff1f9e1f,
|
|
0xff125c12, 0xff1b8a1b, 0xff22ab22, 0xff24b524,
|
|
0xff1A871a, 0xff2de32d, 0xff25bd25, 0xff32ff32
|
|
};
|
|
private static final int[] displayPaletteColor = {
|
|
0xff000000, 0xffdd0033, 0xff000099, 0xffdd22dd,
|
|
0xff007722, 0xff555555, 0xff2222ff, 0xff66aaff,
|
|
0xff885500, 0xffff6600, 0xffaaaaaa, 0xffff9988,
|
|
0xff11dd00, 0xffffff00, 0xff44ff99, 0xffffffff
|
|
};
|
|
|
|
// Character stuff
|
|
private static final int CHARSET_CHAR_SIZE_X = 8;
|
|
private static final int CHARSET_CHAR_SIZE_Y = 8;
|
|
private static final int CHARSET_SIZE_X = 0x100 * CHARSET_CHAR_SIZE_X;
|
|
private static final int CHARSET_SIZE_Y = CHARSET_CHAR_SIZE_Y;
|
|
|
|
private static final int CHARMAP_NORMAL = 0;
|
|
private static final int CHARMAP_FLASH = 1;
|
|
private static final int CHARMAP_ALT = 2;
|
|
|
|
private int[] charSet = new int[CHARSET_SIZE_X * CHARSET_CHAR_SIZE_Y];
|
|
private int[] charMap;
|
|
private int[][] charMaps = new int[3][0x100];
|
|
private long charMapFlashCycle = 0;
|
|
private boolean isCharMapFlash = false;
|
|
|
|
private int[][] charMapLookup = {
|
|
{ 0xc0, 0xa0, 0x40, 0x20, 0x40, 0x20, 0x40, 0x60 },
|
|
{ 0xc0, 0xa0, 0xc0, 0xa0, 0x40, 0x20, 0x40, 0x60 },
|
|
{ 0xc0, 0xa0, 0x00, 0xe0, 0x40, 0x20, 0x40, 0x60 },
|
|
};
|
|
|
|
private static final int[] textLineAddress = {
|
|
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
|
|
0x0028, 0x00a8, 0x0128, 0x01a8, 0x0228, 0x02a8, 0x0328, 0x03a8,
|
|
0x0050, 0x00d0, 0x0150, 0x01d0, 0x0250, 0x02d0, 0x0350, 0x03d0,
|
|
};
|
|
|
|
// Hires stuff
|
|
private int hiresEvenOddToWord[] = new int[0x200];
|
|
private int hiresWord[] = new int[8];
|
|
private int hiresWordNext[] = new int[8];
|
|
private int hiresLookup[] = new int[0x100]; // Bits: [NNccccPP] - Next, current, Previous bits
|
|
private static final int hiresLookupColor[] = {
|
|
// Bits: [PPNNcccc] - Previous, Next, current bits => 4 pixel @ 4 bit color output
|
|
// Color-bleeding algorithm
|
|
0x0000, 0x0111, 0x2222, 0x2333, 0x4440, 0x4551, 0x6662, 0x6773, 0x8800, 0x8911, 0xaa22, 0xab33, 0xcc40, 0xcd51, 0xee62, 0xef73, // 00cccc00
|
|
0x1000, 0x1111, 0x3222, 0x3333, 0x5440, 0x5551, 0x7662, 0x7773, 0x9800, 0x9911, 0xba22, 0xbb33, 0xdc40, 0xdd51, 0xfe62, 0xff73, // 01cccc00
|
|
0x0000, 0x0111, 0x2222, 0x2333, 0x4440, 0x4551, 0x6662, 0x6773, 0x8800, 0x8911, 0xaa22, 0xab33, 0xcc40, 0xcd51, 0xee62, 0xef73, // 10cccc00
|
|
0x1000, 0x1111, 0x3222, 0x3333, 0x5440, 0x5551, 0x7662, 0x7773, 0x9800, 0x9911, 0xba22, 0xbb33, 0xdc40, 0xdd51, 0xfe62, 0xff73, // 11cccc00
|
|
0x0004, 0x0115, 0x2226, 0x2337, 0x4444, 0x4555, 0x6666, 0x6777, 0x8804, 0x8915, 0xaa26, 0xab37, 0xcc44, 0xcd55, 0xee66, 0xef77, // 00cccc01
|
|
0x1004, 0x1115, 0x3226, 0x3337, 0x5444, 0x5555, 0x7666, 0x7777, 0x9804, 0x9915, 0xba26, 0xbb37, 0xdc44, 0xdd55, 0xfe66, 0xff77, // 01cccc01
|
|
0x0004, 0x0115, 0x2226, 0x2337, 0x4444, 0x4555, 0x6666, 0x6777, 0x8804, 0x8915, 0xaa26, 0xab37, 0xcc44, 0xcd55, 0xee66, 0xef77, // 10cccc01
|
|
0x1004, 0x1115, 0x3226, 0x3337, 0x5444, 0x5555, 0x7666, 0x7777, 0x9804, 0x9915, 0xba26, 0xbb37, 0xdc44, 0xdd55, 0xfe66, 0xff77, // 11cccc01
|
|
0x0088, 0x0199, 0x22aa, 0x23bb, 0x44c8, 0x45d9, 0x66ea, 0x67fb, 0x8888, 0x8999, 0xaaaa, 0xabbb, 0xccc8, 0xcdd9, 0xeeea, 0xeffb, // 00cccc10
|
|
0x1088, 0x1199, 0x32aa, 0x33bb, 0x54c8, 0x55d9, 0x76ea, 0x77fb, 0x9888, 0x9999, 0xbaaa, 0xbbbb, 0xdcc8, 0xddd9, 0xfeea, 0xfffb, // 01cccc10
|
|
0x0088, 0x0199, 0x22aa, 0x23bb, 0x44c8, 0x45d9, 0x66ea, 0x67fb, 0x8888, 0x8999, 0xaaaa, 0xabbb, 0xccc8, 0xcdd9, 0xeeea, 0xeffb, // 10cccc10
|
|
0x1088, 0x1199, 0x32aa, 0x33bb, 0x54c8, 0x55d9, 0x76ea, 0x77fb, 0x9888, 0x9999, 0xbaaa, 0xbbbb, 0xdcc8, 0xddd9, 0xfeea, 0xfffb, // 11cccc10
|
|
0x008c, 0x019d, 0x22ae, 0x23bf, 0x44cc, 0x45dd, 0x66ee, 0x67ff, 0x888c, 0x899d, 0xaaae, 0xabbf, 0xcccc, 0xcddd, 0xeeee, 0xefff, // 00cccc11
|
|
0x108c, 0x119d, 0x32ae, 0x33bf, 0x54cc, 0x55dd, 0x76ee, 0x77ff, 0x988c, 0x999d, 0xbaae, 0xbbbf, 0xdccc, 0xdddd, 0xfeee, 0xffff, // 01cccc11
|
|
0x008c, 0x019d, 0x22ae, 0x23bf, 0x44cc, 0x45dd, 0x66ee, 0x67ff, 0x888c, 0x899d, 0xaaae, 0xabbf, 0xcccc, 0xcddd, 0xeeee, 0xefff, // 10cccc11
|
|
0x108c, 0x119d, 0x32ae, 0x33bf, 0x54cc, 0x55dd, 0x76ee, 0x77ff, 0x988c, 0x999d, 0xbaae, 0xbbbf, 0xdccc, 0xdddd, 0xfeee, 0xffff, // 11cccc11
|
|
|
|
// First table
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 00cccc00
|
|
0x0000, 0x1111, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 01cccc00
|
|
0x0000, 0x0001, 0x2222, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 10cccc00
|
|
0x0000, 0x0001, 0x0020, 0x3333, 0x0400, 0x0505, 0x0660, 0x0777, 0xf000, 0x9009, 0xa0a0, 0xb0bb, 0xff00, 0xdd0d, 0xfff0, 0xffff, // 11cccc00
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x4444, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 00cccc01
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x5555, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 01cccc01
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x6666, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 10cccc01
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x7777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 11cccc01
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0fff, 0x8888, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 00cccc10
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9999, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 01cccc10
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xaaaa, 0xb0bb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 10cccc10
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xbbbb, 0xcc00, 0xdd0d, 0xeee0, 0xffff, // 11cccc10
|
|
0x0000, 0x000f, 0x0020, 0x00ff, 0x0400, 0x0505, 0x0660, 0x0fff, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcccc, 0xdd0d, 0xeee0, 0xffff, // 00cccc11
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdddd, 0xeee0, 0xffff, // 01cccc11
|
|
0x0000, 0x0001, 0x0020, 0x0033, 0x0400, 0x0505, 0x0660, 0x0777, 0x8000, 0x9009, 0xa0a0, 0xb0bb, 0xcc00, 0xdd0d, 0xeeee, 0xffff, // 10cccc11
|
|
0x0000, 0x000f, 0x00f0, 0x00ff, 0x0f00, 0x0f0f, 0x0ff0, 0x0fff, 0xf000, 0xf00f, 0xf0f0, 0xf0ff, 0xff00, 0xff0f, 0xfff0, 0xffff, // 11cccc11
|
|
};
|
|
private int[] doubleHiresPalette;
|
|
private static final int[] doubleHiresPaletteColor = {
|
|
0x000000, 0x000099, 0x007722, 0x2222ff,
|
|
0x885500, 0xaaaaaa, 0x11dd00, 0x44ff99,
|
|
0xdd0033, 0xdd22dd, 0x555555, 0x66aaff,
|
|
0xff6600, 0xff9988, 0xffff00, 0xffffff,
|
|
};
|
|
|
|
// Thread stuff
|
|
private boolean isPaused = true;
|
|
private Thread thread;
|
|
|
|
/**
|
|
* AppleDisplay class constructor
|
|
*
|
|
* @param apple The EmAppleII instance
|
|
*/
|
|
public AppleDisplay(EmAppleII apple) {
|
|
this.apple = apple;
|
|
|
|
// Create display image
|
|
displayImageBuffer = new int[DISPLAY_SIZE_X * DISPLAY_SIZE_Y];
|
|
|
|
// Character maps
|
|
precalcCharMaps();
|
|
|
|
// Hires
|
|
precalcHiresEvenEddToWord();
|
|
|
|
// Set parameters
|
|
setScale(1.0f);
|
|
setRefreshRate(10);
|
|
setColorMode(COLORMODE_GREEN);
|
|
requestRefresh();
|
|
}
|
|
|
|
/**
|
|
* Set scale
|
|
*/
|
|
public void setScale(float value) {
|
|
if (value <= 0.0f) {
|
|
return;
|
|
}
|
|
|
|
displayScale = value;
|
|
isPrecalcRequested = true;
|
|
}
|
|
|
|
/**
|
|
* Get scale
|
|
*/
|
|
public float getScale() {
|
|
return displayScale;
|
|
}
|
|
|
|
/**
|
|
* Set refresh rate
|
|
*
|
|
* @param value Display refresh rate in mHz
|
|
*/
|
|
public void setRefreshRate(int value) {
|
|
if (value <= 0.0f) {
|
|
return;
|
|
}
|
|
|
|
this.refreshRate = value;
|
|
refreshInterval = (int) (1000.0 / value);
|
|
}
|
|
|
|
/**
|
|
* Get refresh rate
|
|
*/
|
|
public int getRefreshRate() {
|
|
return refreshRate;
|
|
}
|
|
|
|
/**
|
|
* Set color mode
|
|
*/
|
|
public void setColorMode(int value) {
|
|
colorMode = value;
|
|
isPrecalcRequested = true;
|
|
}
|
|
|
|
/**
|
|
* Get color mode
|
|
*/
|
|
public int getColorMode() {
|
|
return colorMode;
|
|
}
|
|
|
|
/**
|
|
* Set paused
|
|
*/
|
|
public void setPaused(boolean value) {
|
|
if (isPaused == value) {
|
|
return;
|
|
}
|
|
|
|
isPaused = value;
|
|
if (isPaused) {
|
|
try {
|
|
thread.join(1000);
|
|
} catch (InterruptedException e) {
|
|
}
|
|
apple.view.repaint();
|
|
} else {
|
|
isRefreshRequested = true;
|
|
thread = new Thread(this);
|
|
thread.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get paused
|
|
*/
|
|
public boolean isPaused() {
|
|
return isPaused;
|
|
}
|
|
|
|
public void requestRefresh() {
|
|
isRefreshRequested = true;
|
|
}
|
|
|
|
/**
|
|
* Step instruction in debug mode
|
|
*/
|
|
public String getStatInfo() {
|
|
StringBuffer statInfo = new StringBuffer();
|
|
long refreshRateCurrent;
|
|
|
|
// Calculate effective CPU speed
|
|
if (refreshDelayPerSecond > 1000) {
|
|
refreshRateCurrent = refreshRate * 1000 / refreshDelayPerSecond;
|
|
} else {
|
|
refreshRateCurrent = refreshRate;
|
|
}
|
|
|
|
// Return FPS
|
|
statInfo.append(" FPS=").append(refreshRateCurrent).append(" [").append(refreshDelayPerSecond).append(" ms/s]\n");
|
|
statInfo.append(" GM=").append(graphicsMode).append("\n");
|
|
|
|
statInfo.append(" ").append((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024L).append("KB/").append(Runtime.getRuntime().totalMemory() / 1024L).append("KB");
|
|
|
|
return statInfo.toString();
|
|
}
|
|
|
|
/**
|
|
* Display refresh thread
|
|
*/
|
|
public void run() {
|
|
try {
|
|
while (!isPaused) {
|
|
long refreshStart = System.currentTimeMillis();
|
|
long refreshDelay;
|
|
|
|
refreshDisplay();
|
|
|
|
refreshDelay = System.currentTimeMillis() - refreshStart;
|
|
|
|
refreshDelayCumulative += refreshDelay;
|
|
refreshCycle++;
|
|
if (refreshCycle >= refreshRate) {
|
|
refreshDelayPerSecond = refreshDelayCumulative;
|
|
refreshDelayCumulative = refreshCycle = 0;
|
|
}
|
|
|
|
if (refreshDelay < refreshInterval) {
|
|
Thread.sleep(refreshInterval - refreshDelay);
|
|
}
|
|
}
|
|
} catch (Throwable e) {
|
|
apple.view.debug(e);
|
|
apple.view.repaint();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display refresh
|
|
*/
|
|
private void refreshDisplay() {
|
|
boolean isCharsetUpdateRequested = false;
|
|
boolean isSetDirtyRequested = false;
|
|
boolean isSetHiresDirtyRequested = false;
|
|
boolean isRenderRequested = false;
|
|
|
|
// Precalculation
|
|
if (isPrecalcRequested) {
|
|
isPrecalcRequested = false;
|
|
precalcDisplay();
|
|
graphicsMode = -1;
|
|
}
|
|
|
|
// Repaint if graphics mode changes
|
|
if (graphicsMode != apple.graphicsMode) {
|
|
graphicsMode = apple.graphicsMode;
|
|
isCharsetUpdateRequested = true;
|
|
isSetHiresDirtyRequested = true;
|
|
}
|
|
|
|
// Periodic refresh
|
|
if (charMapFlashCycle <= 0) {
|
|
charMapFlashCycle = refreshRate / 4 - 1;
|
|
isCharMapFlash = !isCharMapFlash;
|
|
isCharsetUpdateRequested = true;
|
|
} else {
|
|
charMapFlashCycle--;
|
|
}
|
|
|
|
// Some internal variables
|
|
boolean isSomeText = ((graphicsMode & (EmAppleII.GR_TEXT | EmAppleII.GR_MIXMODE)) != 0);
|
|
boolean isSomeLores = ((graphicsMode & (EmAppleII.GR_TEXT | EmAppleII.GR_HIRES)) == 0);
|
|
boolean isSomeHires = ((graphicsMode & (EmAppleII.GR_TEXT | EmAppleII.GR_HIRES)) == EmAppleII.GR_HIRES);
|
|
|
|
boolean isMixedMode = ((graphicsMode & (EmAppleII.GR_TEXT | EmAppleII.GR_MIXMODE)) == EmAppleII.GR_MIXMODE);
|
|
boolean isPage2 = ((graphicsMode & (EmAppleII.GR_80STORE | EmAppleII.GR_PAGE2)) == EmAppleII.GR_PAGE2);
|
|
boolean isAltChar = ((graphicsMode & EmAppleII.GR_ALTCHAR) != 0);
|
|
boolean isDoubleTextMode = ((graphicsMode & EmAppleII.GR_80CHAR) == EmAppleII.GR_80CHAR);
|
|
boolean isDoubleGraphicsMode = ((graphicsMode & (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES)) == (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES));
|
|
|
|
int baseAddressText = isPage2 ? EmAppleII.MEM_MAIN_RAM2 : EmAppleII.MEM_MAIN_TEXT;
|
|
int baseAddressHires = isPage2 ? EmAppleII.MEM_MAIN_RAM3 : EmAppleII.MEM_MAIN_HIRES;
|
|
|
|
// Set char map
|
|
if (isCharsetUpdateRequested) {
|
|
if (isAltChar) {
|
|
setCharMap(CHARMAP_ALT);
|
|
} else if (isCharMapFlash) {
|
|
setCharMap(CHARMAP_FLASH);
|
|
} else {
|
|
setCharMap(CHARMAP_NORMAL);
|
|
}
|
|
|
|
isSetDirtyRequested = true;
|
|
isRefreshRequested = true;
|
|
}
|
|
|
|
// Refresh dirty buffers?
|
|
if (isSetDirtyRequested) {
|
|
if (isSomeText || isSomeLores) {
|
|
setTextBufferDirty(baseAddressText);
|
|
}
|
|
|
|
if (isSetHiresDirtyRequested && isSomeHires) {
|
|
setHiresBufferDirty(baseAddressHires);
|
|
}
|
|
|
|
isRenderRequested = true;
|
|
} else {
|
|
if ((isSomeText || isSomeLores) && isTextBufferDirty(baseAddressText)) {
|
|
isRenderRequested = true;
|
|
}
|
|
|
|
if (isSomeHires && isHiresBufferDirty(baseAddressHires)) {
|
|
isRenderRequested = true;
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
if (isRenderRequested) {
|
|
if (isSomeText) {
|
|
if (isDoubleTextMode) {
|
|
renderDoubleText(baseAddressText, isMixedMode);
|
|
} else {
|
|
renderText(baseAddressText, isMixedMode);
|
|
}
|
|
}
|
|
|
|
if (isSomeHires) {
|
|
if (isDoubleGraphicsMode) {
|
|
renderDoubleHires(baseAddressHires, isMixedMode);
|
|
} else {
|
|
renderHires(baseAddressHires, isMixedMode);
|
|
}
|
|
} else if (isSomeLores) {
|
|
if (isDoubleGraphicsMode) {
|
|
renderDoubleLores(baseAddressText, isMixedMode);
|
|
} else {
|
|
renderLores(baseAddressText, isMixedMode);
|
|
}
|
|
}
|
|
|
|
isRefreshRequested = true;
|
|
}
|
|
|
|
if (isRefreshRequested) {
|
|
isRefreshRequested = false;
|
|
apple.view.repaint();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set text buffer dirty
|
|
*/
|
|
private void setTextBufferDirty(int baseAddress) {
|
|
// Update dirty
|
|
int addressStart = baseAddress >> 7;
|
|
int addressEnd = addressStart + 8;
|
|
for (int address = addressStart; address < addressEnd; address++) {
|
|
graphicsDirty[address] = true;
|
|
apple.graphicsDirty[address] = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set hires buffer dirty
|
|
*/
|
|
private void setHiresBufferDirty(int baseAddress) {
|
|
// Update dirty
|
|
int addressStart = baseAddress >> 7;
|
|
int addressEnd = addressStart + 8;
|
|
for (int address = addressStart; address < addressEnd; address++) {
|
|
graphicsDirty[address] = true;
|
|
apple.graphicsDirty[address + (0x0000 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0400 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0800 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0c00 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1000 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1400 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1800 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1c00 >> 7)] = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Is text buffer dirty?
|
|
*/
|
|
private boolean isTextBufferDirty(int baseAddress) {
|
|
boolean isDirty = false;
|
|
|
|
// Update dirty
|
|
int addressStart = baseAddress >> 7;
|
|
int addressEnd = addressStart + 8;
|
|
for (int address = addressStart; address < addressEnd; address++) {
|
|
graphicsDirty[address] = apple.graphicsDirty[address];
|
|
apple.graphicsDirty[address] = false;
|
|
if (graphicsDirty[address]) {
|
|
isDirty = true;
|
|
}
|
|
}
|
|
|
|
return isDirty;
|
|
}
|
|
|
|
/**
|
|
* Is hires buffer dirty?
|
|
*/
|
|
private boolean isHiresBufferDirty(int baseAddress) {
|
|
boolean isDirty = false;
|
|
|
|
// Update dirty
|
|
int addressStart = baseAddress >> 7;
|
|
int addressEnd = addressStart + 8;
|
|
for (int address = addressStart; address < addressEnd; address++) {
|
|
graphicsDirty[address] =
|
|
apple.graphicsDirty[address + (0x0000 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x0400 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x0800 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x0c00 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x1000 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x1400 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x1800 >> 7)] ||
|
|
apple.graphicsDirty[address + (0x1c00 >> 7)];
|
|
apple.graphicsDirty[address + (0x0000 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0400 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0800 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x0c00 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1000 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1400 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1800 >> 7)] = false;
|
|
apple.graphicsDirty[address + (0x1c00 >> 7)] = false;
|
|
if (graphicsDirty[address]) {
|
|
isDirty = true;
|
|
}
|
|
}
|
|
|
|
return isDirty;
|
|
}
|
|
|
|
/**
|
|
* Display precalculation
|
|
*/
|
|
private void precalcDisplay() {
|
|
// Display scaled size
|
|
apple.view.setDisplayScaledSizeX((int) (DISPLAY_SIZE_X * displayScale / 2));
|
|
apple.view.setDisplayScaledSizeY((int) (DISPLAY_SIZE_Y * displayScale));
|
|
|
|
// Prepare display palette
|
|
setDisplayPalette();
|
|
|
|
// Prepare character set
|
|
loadCharSet();
|
|
|
|
// Prepare hires graphics
|
|
precalcHiresLookup();
|
|
}
|
|
|
|
private static final int CHARSET_SOURCE_CHAR_COUNT = 128;
|
|
private static final int CHARSET_SOURCE_SIZE_X = CHARSET_SOURCE_CHAR_COUNT * CHARSET_CHAR_SIZE_X;
|
|
private static final int CHARSET_SOURCE_SIZE_Y = CHARSET_CHAR_SIZE_Y;
|
|
|
|
/**
|
|
* Precalculate charSet
|
|
*/
|
|
private void loadCharSet() {
|
|
int charSetOffset = 0;
|
|
|
|
// Get RGB image
|
|
Object resource = apple.view.getCharSet(charSet, CHARSET_SOURCE_SIZE_X, CHARSET_SOURCE_SIZE_Y, CHARSET_SIZE_X);
|
|
|
|
// Duplicate and invert
|
|
for (int charSetPosY = 0; charSetPosY < CHARSET_SIZE_Y; charSetPosY++) {
|
|
for (int charSetPosX = 0; charSetPosX < CHARSET_SOURCE_SIZE_X; charSetPosX++) {
|
|
charSet[charSetOffset + CHARSET_SOURCE_SIZE_X + charSetPosX] =
|
|
charSet[charSetOffset + charSetPosX] ^ 0xffffff;
|
|
}
|
|
charSetOffset += CHARSET_SIZE_X;
|
|
}
|
|
|
|
// Colorize
|
|
for (charSetOffset = 0; charSetOffset < (CHARSET_SIZE_X * CHARSET_SIZE_Y); charSetOffset++) {
|
|
charSet[charSetOffset] &= (displayPalette[0xf] | 0xff000000);
|
|
}
|
|
|
|
//apple.view.debug("here");
|
|
apple.view.flushCharSet(resource);
|
|
}
|
|
|
|
/**
|
|
* Precalculate char map
|
|
*/
|
|
private void precalcCharMaps() {
|
|
for (int index = 0; index < 3; index++) {
|
|
for (int character = 0; character < 0x100; character++) {
|
|
charMaps[index][character] = charMapLookup[index][character >> 5] + (character & 0x1f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set char map
|
|
*/
|
|
private void setCharMap(int value) {
|
|
charMap = charMaps[value];
|
|
}
|
|
|
|
/**
|
|
* Set lores palette
|
|
*/
|
|
private void setDisplayPalette() {
|
|
displayPalette = (colorMode == COLORMODE_COLOR) ? displayPaletteColor : displayPaletteGreen;
|
|
doubleHiresPalette = (colorMode == COLORMODE_COLOR) ? doubleHiresPaletteColor : displayPaletteGreen;
|
|
}
|
|
|
|
/**
|
|
* Precalculate hires even odd to word
|
|
*/
|
|
private void precalcHiresEvenEddToWord() {
|
|
for (int value = 0; value < 0x200; value++) {
|
|
hiresEvenOddToWord[value] =
|
|
((value & 0x01) << 0) |
|
|
((value & 0x01) << 1) |
|
|
((value & 0x02) << 1) |
|
|
((value & 0x02) << 2) |
|
|
((value & 0x04) << 2) |
|
|
((value & 0x04) << 3) |
|
|
((value & 0x08) << 3) |
|
|
((value & 0x08) << 4) |
|
|
((value & 0x10) << 4) |
|
|
((value & 0x10) << 5) |
|
|
((value & 0x20) << 5) |
|
|
((value & 0x20) << 6) |
|
|
((value & 0x40) << 6) |
|
|
((value & 0x40) << 7);
|
|
|
|
if ((value & 0x80) != 0) {
|
|
hiresEvenOddToWord[value] <<= 1;
|
|
hiresEvenOddToWord[value] |= ((value & 0x100) >> 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Precalculate hires
|
|
*/
|
|
private void precalcHiresLookup() {
|
|
if (colorMode == COLORMODE_COLOR) {
|
|
for (int value = 0; value < 0x100; value++) {
|
|
hiresLookup[value] = hiresLookupColor[((value << 6) & 0xff) | (value >> 2)];
|
|
}
|
|
} else {
|
|
for (int value = 0; value < 0x100; value++) {
|
|
hiresLookup[value] =
|
|
(((value & 0x04) != 0) ? 0x000f : 0) |
|
|
(((value & 0x08) != 0) ? 0x00f0 : 0) |
|
|
(((value & 0x10) != 0) ? 0x0f00 : 0) |
|
|
(((value & 0x20) != 0) ? 0xf000 : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render text canvas
|
|
*/
|
|
private final void renderTextScanLine(int destOffset, int sourceOffset) {
|
|
displayImageBuffer[destOffset + 0] = displayImageBuffer[destOffset + 1] = charSet[sourceOffset];
|
|
displayImageBuffer[destOffset + 2] = displayImageBuffer[destOffset + 3] = charSet[sourceOffset + 1];
|
|
displayImageBuffer[destOffset + 4] = displayImageBuffer[destOffset + 5] = charSet[sourceOffset + 2];
|
|
displayImageBuffer[destOffset + 6] = displayImageBuffer[destOffset + 7] = charSet[sourceOffset + 3];
|
|
displayImageBuffer[destOffset + 8] = displayImageBuffer[destOffset + 9] = charSet[sourceOffset + 4];
|
|
displayImageBuffer[destOffset + 10] = displayImageBuffer[destOffset + 11] = charSet[sourceOffset + 5];
|
|
displayImageBuffer[destOffset + 12] = displayImageBuffer[destOffset + 13] = charSet[sourceOffset + 6];
|
|
}
|
|
|
|
private final void renderTextCharacter(int destOffset, int sourceOffset) {
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderTextScanLine(destOffset, sourceOffset);
|
|
}
|
|
|
|
private void renderText(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
for (screenCharY = screenCharYStart; screenCharY < 24; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
for (address = addressStart; address < addressEnd; address++) {
|
|
renderTextCharacter(displayOffset, charMap[apple.mem[address] & 0xff] << 3);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X * 2;
|
|
}
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render double text canvas
|
|
*/
|
|
private final void renderDoubleTextScanLine(int destOffset, int sourceOffset) {
|
|
displayImageBuffer[destOffset + 0] = charSet[sourceOffset];
|
|
displayImageBuffer[destOffset + 1] = charSet[sourceOffset + 1];
|
|
displayImageBuffer[destOffset + 2] = charSet[sourceOffset + 2];
|
|
displayImageBuffer[destOffset + 3] = charSet[sourceOffset + 3];
|
|
displayImageBuffer[destOffset + 4] = charSet[sourceOffset + 4];
|
|
displayImageBuffer[destOffset + 5] = charSet[sourceOffset + 5];
|
|
displayImageBuffer[destOffset + 6] = charSet[sourceOffset + 6];
|
|
}
|
|
|
|
private final void renderDoubleTextCharacter(int destOffset, int sourceOffset) {
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
destOffset += DISPLAY_SIZE_X; sourceOffset += CHARSET_SIZE_X;
|
|
renderDoubleTextScanLine(destOffset, sourceOffset);
|
|
}
|
|
|
|
private void renderDoubleText(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
for (screenCharY = screenCharYStart; screenCharY < 24; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
for (address = addressStart; address < addressEnd; address++) {
|
|
renderDoubleTextCharacter(displayOffset, charMap[apple.mem[address + 0x10000] & 0xff] << 3);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
renderDoubleTextCharacter(displayOffset, charMap[apple.mem[address + 0x00000] & 0xff] << 3);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
}
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render lores canvas
|
|
*/
|
|
private final void renderLoresScanLine(int destOffset, int color) {
|
|
displayImageBuffer[destOffset + 0] = color;
|
|
displayImageBuffer[destOffset + 1] = color;
|
|
displayImageBuffer[destOffset + 2] = color;
|
|
displayImageBuffer[destOffset + 3] = color;
|
|
displayImageBuffer[destOffset + 4] = color;
|
|
displayImageBuffer[destOffset + 5] = color;
|
|
displayImageBuffer[destOffset + 6] = color;
|
|
}
|
|
|
|
private final void renderLoresBlock(int destOffset, int colorTop, int colorBottom) {
|
|
renderLoresScanLine(destOffset, colorTop);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorTop);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorTop);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorTop);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorBottom);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorBottom);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorBottom);
|
|
destOffset += DISPLAY_SIZE_X;
|
|
renderLoresScanLine(destOffset, colorBottom);
|
|
}
|
|
|
|
private void renderLores(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = 0;
|
|
for (screenCharY = 0; screenCharY < screenCharYEnd; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
for (address = addressStart; address < addressEnd; address++) {
|
|
renderLoresBlock(displayOffset,
|
|
displayPalette[apple.mem[address] & 0xf],
|
|
displayPalette[(apple.mem[address] & 0xf0) >> 4]);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
renderLoresBlock(displayOffset,
|
|
displayPalette[apple.mem[address] & 0xf],
|
|
displayPalette[(apple.mem[address] & 0xf0) >> 4]);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
}
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render double lores canvas
|
|
*/
|
|
private void renderDoubleLores(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = 0;
|
|
for (screenCharY = 0; screenCharY < screenCharYEnd; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
for (address = addressStart; address < addressEnd; address++) {
|
|
renderLoresBlock(displayOffset,
|
|
displayPalette[apple.mem[address + 0x10000] & 0xf],
|
|
displayPalette[(apple.mem[address + 0x10000] & 0xf0) >> 4]);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
renderLoresBlock(displayOffset,
|
|
displayPalette[apple.mem[address] & 0xf],
|
|
displayPalette[(apple.mem[address] & 0xf0) >> 4]);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X;
|
|
}
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render hires canvas
|
|
*/
|
|
private final void renderHiresWord(int destOffset, int hiresNibble) {
|
|
displayImageBuffer[destOffset + 0] = displayPalette[(hiresNibble >> 0) & 0xf];
|
|
displayImageBuffer[destOffset + 1] = displayPalette[(hiresNibble >> 4) & 0xf];
|
|
displayImageBuffer[destOffset + 2] = displayPalette[(hiresNibble >> 8) & 0xf];
|
|
displayImageBuffer[destOffset + 3] = displayPalette[(hiresNibble >> 12) & 0xf];
|
|
}
|
|
|
|
private final void renderHiresScanLine(int destOffset, int hiresWord) {
|
|
renderHiresWord(destOffset + 0, hiresLookup[(hiresWord >> 0) & 0xff]);
|
|
renderHiresWord(destOffset + 4, hiresLookup[(hiresWord >> 4) & 0xff]);
|
|
renderHiresWord(destOffset + 8, hiresLookup[(hiresWord >> 8) & 0xff]);
|
|
renderHiresWord(destOffset + 12, hiresLookup[(hiresWord >> 12) & 0xff]);
|
|
renderHiresWord(destOffset + 16, hiresLookup[(hiresWord >> 16) & 0xff]);
|
|
renderHiresWord(destOffset + 20, hiresLookup[(hiresWord >> 20) & 0xff]);
|
|
renderHiresWord(destOffset + 24, hiresLookup[(hiresWord >> 24) & 0xff]);
|
|
}
|
|
|
|
private final void renderHiresBlock(int destOffset) {
|
|
renderHiresScanLine(destOffset, hiresWord[0]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[1]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[2]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[3]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[4]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[5]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[6]); destOffset += DISPLAY_SIZE_X;
|
|
renderHiresScanLine(destOffset, hiresWord[7]);
|
|
}
|
|
|
|
private final void resetHiresWords() {
|
|
hiresWord[0] = 0;
|
|
hiresWord[1] = 0;
|
|
hiresWord[2] = 0;
|
|
hiresWord[3] = 0;
|
|
hiresWord[4] = 0;
|
|
hiresWord[5] = 0;
|
|
hiresWord[6] = 0;
|
|
hiresWord[7] = 0;
|
|
}
|
|
|
|
private final void bufferHiresWords() {
|
|
hiresWord[0] = hiresWordNext[0];
|
|
hiresWord[1] = hiresWordNext[1];
|
|
hiresWord[2] = hiresWordNext[2];
|
|
hiresWord[3] = hiresWordNext[3];
|
|
hiresWord[4] = hiresWordNext[4];
|
|
hiresWord[5] = hiresWordNext[5];
|
|
hiresWord[6] = hiresWordNext[6];
|
|
hiresWord[7] = hiresWordNext[7];
|
|
}
|
|
|
|
private final void calcNextHiresWord(int hiresWordIndex, int byteEven, int byteOdd) {
|
|
hiresWordNext[hiresWordIndex] = hiresWord[hiresWordIndex] >> 28;
|
|
hiresWordNext[hiresWordIndex] |=
|
|
hiresEvenOddToWord[(byteEven & 0xff) | ((hiresWordNext[hiresWordIndex] & 0x2) << 7)] << 2;
|
|
hiresWordNext[hiresWordIndex] |=
|
|
hiresEvenOddToWord[(byteOdd & 0xff) | ((hiresWordNext[hiresWordIndex] & 0x8000) >> 7)] << 16;
|
|
hiresWord[hiresWordIndex] |= (hiresWordNext[hiresWordIndex] << 28);
|
|
}
|
|
|
|
private final void calcNextHiresWords(int address) {
|
|
calcNextHiresWord(0, apple.mem[address + 0x00000], apple.mem[address + 0x00001]);
|
|
calcNextHiresWord(1, apple.mem[address + 0x00400], apple.mem[address + 0x00401]);
|
|
calcNextHiresWord(2, apple.mem[address + 0x00800], apple.mem[address + 0x00801]);
|
|
calcNextHiresWord(3, apple.mem[address + 0x00c00], apple.mem[address + 0x00c01]);
|
|
calcNextHiresWord(4, apple.mem[address + 0x01000], apple.mem[address + 0x01001]);
|
|
calcNextHiresWord(5, apple.mem[address + 0x01400], apple.mem[address + 0x01401]);
|
|
calcNextHiresWord(6, apple.mem[address + 0x01800], apple.mem[address + 0x01801]);
|
|
calcNextHiresWord(7, apple.mem[address + 0x01c00], apple.mem[address + 0x01c01]);
|
|
}
|
|
|
|
private void renderHires(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = 0;
|
|
for (screenCharY = 0; screenCharY < screenCharYEnd; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
resetHiresWords();
|
|
calcNextHiresWords(addressStart);
|
|
for (address = (addressStart + 2); address < addressEnd; address += 2) {
|
|
bufferHiresWords();
|
|
calcNextHiresWords(address);
|
|
renderHiresBlock(displayOffset);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X * 4;
|
|
}
|
|
bufferHiresWords();
|
|
renderHiresBlock(displayOffset);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X * 4;
|
|
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render double hires canvas
|
|
*/
|
|
private final void renderDoubleHiresWord(int destOffset, int hiresNibble) {
|
|
displayImageBuffer[destOffset + 0] = doubleHiresPalette[(hiresNibble >> 0) & 0xf];
|
|
displayImageBuffer[destOffset + 1] = doubleHiresPalette[(hiresNibble >> 4) & 0xf];
|
|
displayImageBuffer[destOffset + 2] = doubleHiresPalette[(hiresNibble >> 8) & 0xf];
|
|
displayImageBuffer[destOffset + 3] = doubleHiresPalette[(hiresNibble >> 12) & 0xf];
|
|
}
|
|
|
|
private final void renderDoubleHiresScanLine(int destOffset, int hiresWord) {
|
|
renderDoubleHiresWord(destOffset + 0, hiresLookup[(hiresWord >> 0) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 4, hiresLookup[(hiresWord >> 4) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 8, hiresLookup[(hiresWord >> 8) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 12, hiresLookup[(hiresWord >> 12) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 16, hiresLookup[(hiresWord >> 16) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 20, hiresLookup[(hiresWord >> 20) & 0xff]);
|
|
renderDoubleHiresWord(destOffset + 24, hiresLookup[(hiresWord >> 24) & 0xff]);
|
|
}
|
|
|
|
private final void renderDoubleHiresBlock(int destOffset) {
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[0]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[1]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[2]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[3]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[4]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[5]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[6]); destOffset += DISPLAY_SIZE_X;
|
|
renderDoubleHiresScanLine(destOffset, hiresWord[7]);
|
|
}
|
|
|
|
private final void calcNextDoubleHiresWord(int hiresWordIndex, int byte1, int byte2, int byte3, int byte4) {
|
|
hiresWordNext[hiresWordIndex] = (
|
|
((byte1 & 0x7f) << 2) | ((byte2 & 0x7f) << 9) |
|
|
((byte3 & 0x7f) << 16) | ((byte4 & 0x7f) << 23) |
|
|
(hiresWord[hiresWordIndex] >> 28));
|
|
hiresWord[hiresWordIndex] |= (hiresWordNext[hiresWordIndex] << 28);
|
|
}
|
|
|
|
private final void calcNextDoubleHiresWords(int address) {
|
|
calcNextDoubleHiresWord(0,
|
|
apple.mem[address + 0x10000], apple.mem[address + 0x00000],
|
|
apple.mem[address + 0x10001], apple.mem[address + 0x00001]);
|
|
calcNextDoubleHiresWord(1,
|
|
apple.mem[address + 0x10400], apple.mem[address + 0x00400],
|
|
apple.mem[address + 0x10401], apple.mem[address + 0x00401]);
|
|
calcNextDoubleHiresWord(2,
|
|
apple.mem[address + 0x10800], apple.mem[address + 0x00800],
|
|
apple.mem[address + 0x10801], apple.mem[address + 0x00801]);
|
|
calcNextDoubleHiresWord(3,
|
|
apple.mem[address + 0x10c00], apple.mem[address + 0x00c00],
|
|
apple.mem[address + 0x10c01], apple.mem[address + 0x00c01]);
|
|
calcNextDoubleHiresWord(4,
|
|
apple.mem[address + 0x11000], apple.mem[address + 0x01000],
|
|
apple.mem[address + 0x11001], apple.mem[address + 0x01001]);
|
|
calcNextDoubleHiresWord(5,
|
|
apple.mem[address + 0x11400], apple.mem[address + 0x01400],
|
|
apple.mem[address + 0x11401], apple.mem[address + 0x01401]);
|
|
calcNextDoubleHiresWord(6,
|
|
apple.mem[address + 0x11800], apple.mem[address + 0x01800],
|
|
apple.mem[address + 0x11801], apple.mem[address + 0x01801]);
|
|
calcNextDoubleHiresWord(7,
|
|
apple.mem[address + 0x11c00], apple.mem[address + 0x01c00],
|
|
apple.mem[address + 0x11c01], apple.mem[address + 0x01c01]);
|
|
}
|
|
|
|
private void renderDoubleHires(int baseAddress, boolean isMixedMode) {
|
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
|
int displayOffset;
|
|
int address, addressEnd, addressStart;
|
|
|
|
displayOffset = 0;
|
|
for (screenCharY = 0; screenCharY < screenCharYEnd; screenCharY++) {
|
|
addressStart = baseAddress + textLineAddress[screenCharY];
|
|
|
|
if (graphicsDirty[addressStart >> 7]) {
|
|
addressEnd = addressStart + 40;
|
|
|
|
resetHiresWords();
|
|
calcNextDoubleHiresWords(addressStart);
|
|
for (address = (addressStart + 2); address < addressEnd; address += 2) {
|
|
bufferHiresWords();
|
|
calcNextDoubleHiresWords(address);
|
|
renderDoubleHiresBlock(displayOffset);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X * 4;
|
|
}
|
|
bufferHiresWords();
|
|
renderDoubleHiresBlock(displayOffset);
|
|
displayOffset += DISPLAY_CHAR_SIZE_X * 4;
|
|
|
|
displayOffset += (DISPLAY_CHAR_SIZE_Y - 1) * DISPLAY_SIZE_X;
|
|
} else {
|
|
displayOffset += DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
|
}
|
|
}
|
|
}
|
|
}
|