From d220ffd08cbdcc414af8a8b074b0895c8e41b056 Mon Sep 17 00:00:00 2001 From: Peter Jakubco Date: Fri, 28 Apr 2023 10:42:55 +0200 Subject: [PATCH] [#314] zxspectrum-ula: implement flashing --- .../cassette_player/PlaybackListenerImpl.java | 3 ++ .../plugins/device/zxspectrum/ula/ULA.java | 47 ++++++++++++------- .../zxspectrum/ula/gui/DisplayCanvas.java | 10 +++- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/plugins/device/cassette-player/src/main/java/net/emustudio/plugins/device/cassette_player/PlaybackListenerImpl.java b/plugins/device/cassette-player/src/main/java/net/emustudio/plugins/device/cassette_player/PlaybackListenerImpl.java index ffee671c2..68b73d65c 100644 --- a/plugins/device/cassette-player/src/main/java/net/emustudio/plugins/device/cassette_player/PlaybackListenerImpl.java +++ b/plugins/device/cassette-player/src/main/java/net/emustudio/plugins/device/cassette_player/PlaybackListenerImpl.java @@ -26,6 +26,9 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; +// Pulse length: +// https://worldofspectrum.org/faq/reference/48kreference.htm + //Machine Pilot pulse Length Sync1 Sync2 Bit 0 Bit 1 //ZX Spectrum 2168 (1) 667 735 855 1710 public class PlaybackListenerImpl implements Loader.PlaybackListener { diff --git a/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/ULA.java b/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/ULA.java index 7c92e6b7b..fb4137c96 100644 --- a/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/ULA.java +++ b/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/ULA.java @@ -45,15 +45,14 @@ * 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 * 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y * 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - * 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + * 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHIFT, M, N, B *

* The colour attribute data overlays the monochrome bitmap data and is arranged in a linear fashion from left to right, * top to bottom. - * Each attribute byte colours an 8x8 character on the screen and is encoded as follows: + * Each attribute byte colours is 8x8 character on the screen and is encoded as follows: *

- * 7 6 5 4 3 2 1 0 - * +-------------------------------+ - * | F | B | P2| P1| P0| I2| I1| I0| + * 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * F | B | P2| P1| P0| I2| I1| I0| * +-------------------------------+ *

* - F sets the attribute FLASH mode @@ -73,24 +72,35 @@ public class ULA implements Context8080.CpuPortDevice, Keyboard.OnKeyListener { public final byte[][] attributeMemory = new byte[SCREEN_WIDTH][ATTRIBUTE_HEIGHT]; private final static int[] lineStartOffsets = computeLineStartOffsets(); + // The Spectrum's 'FLASH' effect is also produced by the ULA: Every 16 frames, the ink and paper of all flashing + // bytes is swapped; ie a normal to inverted to normal cycle takes 32 frames, which is (good as) 0.64 seconds. + public boolean videoFlash = false; + private int flashFramesCount = 0; + private final ZxSpectrumBus bus; private int borderColor; private boolean microphoneAndEar; - private static int[] computeLineStartOffsets() { - final int[] result = new int[SCREEN_HEIGHT]; - for (int y = 0; y < SCREEN_HEIGHT; y++) { - result[y] = ((y & 0xC0) << 5) | ((y & 7) << 8) | ((y & 0x38) << 2); - } - return result; - } public ULA(ZxSpectrumBus bus) { this.bus = Objects.requireNonNull(bus); Arrays.fill(keymap, (byte) 0xBF); } + public void reset() { + borderColor = 7; + microphoneAndEar = false; + Arrays.fill(keymap, (byte) 0xBF); + } + + public void onNextFrame() { + if (flashFramesCount == 15) { + videoFlash = !videoFlash; + } + flashFramesCount = (flashFramesCount + 1) % 16; + } + public void readScreen() { for (int x = 0; x < SCREEN_WIDTH; x++) { for (int y = 0; y < SCREEN_HEIGHT; y++) { @@ -125,11 +135,7 @@ public void readLine(int y) { } } - public void reset() { - borderColor = 7; - microphoneAndEar = false; - Arrays.fill(keymap, (byte) 0xBF); - } + public int getBorderColor() { return borderColor; @@ -587,4 +593,11 @@ public void onKeyDown(byte data) { } } + private static int[] computeLineStartOffsets() { + final int[] result = new int[SCREEN_HEIGHT]; + for (int y = 0; y < SCREEN_HEIGHT; y++) { + result[y] = ((y & 0xC0) << 5) | ((y & 7) << 8) | ((y & 0x38) << 2); + } + return result; + } } diff --git a/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/gui/DisplayCanvas.java b/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/gui/DisplayCanvas.java index 1e728974d..efc99d413 100644 --- a/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/gui/DisplayCanvas.java +++ b/plugins/device/zxspectrum-ula/src/main/java/net/emustudio/plugins/device/zxspectrum/ula/gui/DisplayCanvas.java @@ -97,6 +97,7 @@ public void start() { createBufferStrategy(2); ted.schedule(REPAINT_CPU_TSTATES, this::triggerCpuInterrupt); + ted.schedule(REPAINT_CPU_TSTATES, ula::onNextFrame); for (int i = 0; i < SCREEN_IMAGE_HEIGHT; i++) { int finalI = i; ted.schedule(i * LINE_CPU_TSTATES + 1, () -> drawNextLine(finalI)); @@ -131,10 +132,17 @@ private void drawNextLine(int line) { byte row = ula.videoMemory[byteX][y]; int attr = ula.attributeMemory[byteX][y / 8]; Color[] colorMap = ((attr & 0x40) == 0x40) ? BRIGHT_COLOR_MAP : COLOR_MAP; + boolean flash = (attr & 0x80) == 0x80; for (int i = 0; i < 8; i++) { boolean bit = ((row << i) & 0x80) == 0x80; - int color = (bit ? colorMap[attr & 7] : colorMap[(attr >>> 3) & 7]).getRGB(); + int color; + if (ula.videoFlash && flash) { + color = (bit ? colorMap[(attr >>> 3) & 7] : colorMap[attr & 7]).getRGB(); + } else { + color = (bit ? colorMap[attr & 7] : colorMap[(attr >>> 3) & 7]).getRGB(); + } + int offset = line * SCREEN_IMAGE_WIDTH + BORDER_WIDTH + screenX + i; screenImageData[offset] = color; }