Merge Fuse 1.9.1 - #65
Merged
Merged
Conversation
Fuse supports both SDL1 and SDL2, and this library can be independently enabled for the UI, audio and joystick support. The problem is that for the UI and audio we have separate sdl1 and sdl2 options in configure.ac. This is confusing and error prone because we should be using the same SDL version for everything. This patch leaves one single SDL UI option (--with-sdl) and one single SDL audio driver (--with-audio-driver=sdl). Fuse tries to use SDL2 if it's available, with SDL1 as a fallback. This can be overridden with the existing --disable-sdl2 option. Changes after this patch: - $use_sdl1 and $use_sdl2 are replace by a single $use_sdl variable that can be set to '1', '2' or 'no'. - $sdl1_available and $sdl2_available are also replaced with $sdl_available using the same logic. - The --with-sdl2 option is removed. - The sdl2 audio driver is removed. The $UI variable still has separate sdl and sdl2 values because it refers to the two different implementations under the ui/ directory.
This file must be generated when Fuse is actually compiled by the final user or distributor with the GTK UI enabled. The version generated during 'make dist' depends on whatever values the local config.h file has at that moment. The result can easily be wrong and should not be put into release tarballs.
The error can be seen with the following command:
LC_ALL=C.UTF-8 MANROFFSEQ='' MANWIDTH=80 \
man --warnings -E UTF-8 -l -Tutf8 -Z man/fuse.1 > /dev/null
Handle partial successful returns from snd_pcm_writei() correctly in the ALSA backend. The old code reduced the remaining frame count after a short write but retried from the start of the same sample buffer. This could cause audio corruption by repeating already-written frames and skipping later ones. Advance the sample pointer by the number of frames written, multiplied by the channel count, before retrying the remaining frames.
autoconf 2.71 requires AC_PROG_LEX to be called with either 'yywrap' or 'noyywrap'; calling it without an argument now emits: configure.ac:82: warning: AC_PROG_LEX without either yywrap or noyywrap is obsolete The debugger's commandl.l scanner uses a single static command buffer and never needs to switch input files, so yywrap() trivially returned 1 (no more input). Replace that idiom with the modern '%option noyywrap' flex directive, which generates the same behaviour directly and does not require an external yywrap symbol.
It fails every night and we are not using releases in the Fuse GitHub mirror account yet.
Update the vendored pkg.m4 autoconf helper from serial 1 (pkg-config 0.24) to serial 12 (pkg-config-0.29.2). The new version adds PKG_PREREQ (version guard for the macros themselves), PKG_CHECK_MODULES_STATIC, PKG_WITH_MODULES, PKG_HAVE_WITH_MODULES, and PKG_HAVE_DEFINE_WITH_MODULES, and improves internal diagnostics and error messages. All existing macros used by configure.ac (PKG_PROG_PKG_CONFIG, PKG_CHECK_MODULES, PKG_CHECK_VAR, PKG_INSTALLDIR, PKG_NOARCH_INSTALLDIR) remain fully compatible.
libjsw is unmaintained and no longer distributed in Linux distributions (except maybe Gentoo). Use SDL to handle joysticks with all UIs.
When GDK_HINT_MAX_SIZE is ignored by a window manager while tiling or maximizing the window, the UI can break if the computed size exceeds MAX_SCALE, giving us a blank window and GTK warnings like these: drawing failure for widget 'GtkWindow': invalid value for stride drawing failure for widget 'GtkDrawingArea': invalid value for stride drawing failure for widget 'GtkBox': invalid value for stride This patch limits the size to MAX_SCALE and centres the surface in the drawing area.
Use SDL keysyms rather than scancodes for SDL2 Spectrum key events. This makes SDL2 keyboard input follow the host keyboard layout, so non-QWERTY layouts such as Dvorak behave more like the old SDL1 UI on Unix-like systems. UI/native key handling is unchanged (thanks, Phil Reynolds).
The autotools variable EXEEXT is usually empty on Unix and '.exe' on Windows. But on Unix it can also be used to change the name of the executable, for example 'make EXEEXT=-sdl' to generate a 'fuse-sdl' binary. With this change the Exec entry in the desktop file reflects the actual name of the Fuse binary.
When the tape is asked to stop (STOP, STOP48, or end of tape) the edge for that final event has not been played yet. tape_next_edge() stops the tape and returns immediately, so the embedded pause at the end of the block is ignored. Some loaders need that final pause, so defer the stop instead and carry it out on the next tape event. This makes all TZX versions of 1942 work with --detect-loader, and Chuckie Egg 2 with --detect-loader --no-accelerate-loader. (fixes bugs #377 and #480)
Fuse only allows the GTK window to be resized in discrete steps, so only window sizes that are a multiple of the Spectrum screen are allowed. This generally works on X11 (with certain exceptions, like tiling window managers and maximized windows). However, Wayland ignores the resize increment hint so windows can be resized freely. Fuse does not expect this, which results in lots of display artifacts (see also commit 70456ce for a related bug). This patch fixes those artifacts by filling the drawing area with black and painting the contents on top. Additionally, when the window is manually resized (which triggers a GTK configure_event) and a scaler is selected, activate it without trying to force a window resize.
Fuse only repaints the parts of the screen that changed each frame and relies on the rest staying intact. uidisplay_frame_end() calls gdk_window_process_updates() to flush the queued partial redraws immediately. That works on X11, but on Wayland this produces artifacts, so drop the call (which has been deprecated for a decade anyway) and let GTK take care of it.
The Wayland backend is now usable enough so there's no need to override the GDK defaults. (fixes bug #367)
(matja and Fredrick Meunier) Filter code is derived from snes_ntsc (by Shay Green). Also, improve Win32 graphics update performance by writing directly into window DIB (Fredrick Meunier).
Add 16 tests covering the remaining ED-prefix instructions that were not yet tested: LD R,A / LD A,R (complementary to existing LD I,A / LD A,I), RRD / RLD, and all compare/IO block instructions (CPI/CPD/CPIR/CPDR, INI/IND/INIR/INDR, OUTI/OUTD/OTIR/OTDR). The opcodes are verified against the disassemble_ed() lookup tables (opcode_01xxx111 and opcode_101xxxxx) in disassemble.c.
Add 26 new tests in three groups: 1. CB-prefix rotations/shifts (tests 72-80): the rotate and shift instructions (RLC/RRC/RL/RR/SLA/SRA/SLL/SRL) had zero test coverage. Adds one test per operation using register B, plus RLC (HL) to exercise the indirect (HL) addressing path in source_reg(). 2. SBC HL,rr / ADC HL,rr — all register pairs (tests 81-86): previously only BC was tested. Adds DE, HL, and SP variants to give full coverage of the reg_pair() lookup used by cases 0x02/0x0a in disassemble_ed(). 3. LD (nn),rr / LD rr,(nn) — all register pairs (tests 87-92): previously only BC was tested. Adds DE, HL, and SP variants for cases 0x03/0x0b in disassemble_ed(). All opcodes verified against the lookup tables in disassemble_cb() and disassemble_ed().
Add 36 tests (93-128) covering IX and IY instruction families that were previously untested: - ADD IX/IY,rr for DE, IX/IY, and SP operands (completing the ADD IX/IY family; BC was already test3/test7) - LD IX/IY,nn (16-bit immediate load) - INC/DEC IX/IY - LD (nn),IX/IY and LD IX/IY,(nn) (indirect 16-bit loads) - PUSH/POP IX/IY - JP (IX/IY), EX (SP),IX/IY, LD SP,IX/IY - Undocumented IXh, IXl, IYh, IYl register forms (LD A,IXh etc) - LD r,(IX/IY+d) and LD (IX/IY+d),r - LD (IX/IY+d),n
scaler_for_size() was introduced in the scaler-selection merge but had no unit test coverage. Add scaler_for_size_test() to unittests.c covering: - all four size positions within the Normal family - lookup from a non-1x family member - size clamping (< 1 → 1, > 4 → 4) - scaler not in any family (SCALER_DOTMATRIX) → returned unchanged - incomplete family registration → scaler returned unchanged - TV family duplicate 1x/2x slot (TV2X appears in both positions) - Timex family (SCALER_NORMAL at 2x position; requesting 1x → SCALER_HALF)
Add five deparse tests that exercise the bracketing logic for comparison and equality operators: - deparse-eq-non-assoc: 3 == (4 == 5) — right '==' at equal EQUALITY precedence: is_non_associative triggers brackets - deparse-lt-non-assoc-right: 3 < (4 < 5) — right '<' at equal COMPARISON precedence: brackets - deparse-lt-non-assoc-left: (3 < 4) < 5 — left '<' at equal COMPARISON precedence: outer op non-associative triggers brackets - deparse-lt-inside-eq: (3 < 4) == 5 — COMPARISON child of EQUALITY parent: child has higher precedence, no brackets needed - deparse-eq-inside-lt: 3 < (4 == 5) — EQUALITY child of COMPARISON parent: child has lower precedence, brackets required The last two tests document the cross-precedence asymmetry: equality (lower precedence) inside a comparison (higher precedence) needs brackets, but comparison inside equality does not.
In display_write_if_dirty_timex(), when in lores (non-hires) mode the attribute byte for the current cell is already stored in data2 (it was read from display_get_attr_byte at the top of the function). The subsequent call to display_get_attr() read it a second time purely to decompose it into ink and paper components. Move the attr decomposition into each branch so that: - lores path: calls display_parse_attr( data2, ... ) directly, avoiding the second display_get_attr_byte read. - hires path: inlines the equivalent display_parse_attr( display_get_attr_byte( x, y ), ... ) since hires_get_attr() must still be consulted. This also removes the now-unused display_get_attr() wrapper (forward declaration and definition), reducing indirection.
The display_dirty_xtable2 and display_dirty_ytable2 static arrays (each 768 libspectrum_word elements = 1536 bytes, 3072 bytes combined) were used solely by display_dirty64() to map an attribute-byte offset to (x, y) coordinates. Because the ZX Spectrum attribute area is laid out linearly (row-major, 32 columns per row), the mapping is trivially expressed as integer arithmetic: x = idx & (DISPLAY_WIDTH_COLS - 1) -- idx % 32 y = (idx / DISPLAY_WIDTH_COLS) * 8 -- row * 8 where idx = offset - DISPLAY_HEIGHT * DISPLAY_WIDTH_COLS. GCC emits 'and' and 'sar'/'shl' for these, with no division instruction. Benefits: - Removes 3072 bytes of static data. - Eliminates two data-cache lines from display_init(). - Makes the intent of the coordinate calculation self-documenting. - Removes the loop that populated the tables from display_init().
…cal operator precedence - debugger/event.c: add const to event_matches() first parameter; use pointer into the GArray instead of copying the struct in debugger_event_is_registered() and debugger_event(). Avoids a copy-on-every-iteration in lookup loops. - debugger/system_variable.c: add const to system_variable_matches() first parameter; use a const pointer in find_system_variable() and copy only when the entry is found and the caller wants it. Add five deparse tests to debugger_expression_unittest() covering precedence interactions that were not previously exercised: - deparse-and-inside-or: '||' with '&&' right child — no brackets needed because '&&' has higher precedence. - deparse-or-inside-and: '&&' with '||' left child — brackets required because '||' has lower precedence than '&&'. - deparse-cmp-inside-and: comparison operators inside '&&' — no brackets since comparison has higher precedence than logical AND. - deparse-and-inside-cmp: '&&' as right operand of '<' — brackets required since '&&' has lower precedence than '<'. - deparse-mul-mod-non-assoc-right: '*' parent with '%' right child — brackets required because '%' is non-associative even at the same precedence level as '*'.
The AY noise RNG update in sound_ay_overlay() used two conditional
branches whose outcomes are pseudo-random:
if( ( rng & 1 ) ^ ( ( rng & 2 ) ? 1 : 0 ) )
if( rng & 1 ) {
rng ^= 0x24000;
}
rng >>= 1;
Because the LFSR output is by definition pseudo-random, a CPU branch
predictor cannot learn a pattern; both branches will mispredict ~50 %
of the time, each costing 10–20 cycles on modern microarchitectures.
Replace with equivalent branch-free expressions:
noise_toggle ^= ( rng ^ ( rng >> 1 ) ) & 1;
rng = ( rng >> 1 ) ^ ( 0x12000 & -( rng & 1 ) );
Correctness:
- Toggle condition: (rng & 1) XOR ((rng & 2) >> 1) == (rng ^ (rng>>1)) & 1
- LFSR step: XOR-then-shift is equivalent to shift-then-XOR-by-half the
original mask (0x24000 >> 1 == 0x12000); the '-( rng & 1 )' idiom
produces an all-ones mask when bit 0 is set and zero otherwise.
The inner loop runs at most once per outer tstate iteration (~109 200
iterations/second at 50 Hz). Eliminating two branch mispredictions per
noise period crossing can save several thousand cycles per second on a
modern out-of-order CPU.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 7f543ae.
On POSIX systems (Linux, macOS), putenv(3) inserts the supplied
string pointer directly into the environment array rather than copying
it. The previous code freed the buffer immediately after the
putenv() call, leaving the SDL_AUDIODRIVER environment variable
pointing at freed memory. Any subsequent getenv("SDL_AUDIODRIVER")
or environ[] access (e.g. inside SDL_Init) would read deallocated
storage, causing undefined behaviour.
Fix by moving libspectrum_free() to the error path only, where
putenv() returned non-zero and the string was never inserted into the
environment. On the success path the comment now documents that
the allocation is intentionally kept alive for the process lifetime,
consistent with POSIX requirements for putenv().
Replace the loop that sets each bit individually with a single arithmetic expression: (1 << DISPLAY_SCREEN_WIDTH_COLS) - 1. This is equivalent in value and intent — a mask with one bit set for every column — but is more concise and directly expresses the meaning without iteration.
…nstants Replace bare hex literals 0x1800, 0x1b00, 0x3800, 0x3b00, and 6144 in display.c with named constants derived from the existing DISPLAY_HEIGHT, DISPLAY_HEIGHT_ROWS, and DISPLAY_WIDTH_COLS definitions. Replace the local MONO_BITMAP_SIZE 6144 definition in screenshot.c with DISPLAY_PIXEL_BYTES from display.h to eliminate the duplicate literal. Also replace the 32 literals in the dirty table initialisation loop with DISPLAY_WIDTH_COLS for consistency with the rest of the file. No functional change.
The |& pipe operator (bash 4+) was the only reason macOS CI jobs had to run 'brew install bash' before configuring Fuse. Replace it with the POSIX-compatible '2>&1 |' across all three build sub-workflows so that the system bash (3.x on macOS) works without any Homebrew upgrade. - build_macos_sub.yml: remove the 'brew install bash' block; replace '|& tee' with '2>&1 | tee' in the configure step - build_linux_sub.yml: same '2>&1 | tee' substitution for consistency - build_windows_sub.yml: same '2>&1 | tee' substitution for consistency This reduces macOS CI time by eliminating one Homebrew installation.
errno is always a positive value; passing -errno to strerror() yields "Unknown error" instead of the real OS error string. Fix both the unix and linux implementations.
*var is initialised to 0 at the top of the function. The else branch in the inner conditional therefore always sets *var to a value it already holds, and can be dropped. Combine the two-level if into a single boolean expression.
Replace 6912 / 0x1b00 / 6144 magic numbers with the newly introduced DISPLAY_FILE_SIZE and DISPLAY_PIXEL_BYTES macros from display.h. - screenshot.h: STANDARD_SCR_SIZE now defined in terms of DISPLAY_FILE_SIZE; add #include "display.h" so the constant resolves without requiring the includer to pull it in separately. - slt.c: four occurrences of 6912 (SLT screen allocation / copy) become DISPLAY_FILE_SIZE; add #include "display.h". - memory_pages.c: two 0x1b00 comparisons in the display-dirty helpers become DISPLAY_FILE_SIZE (display.h already included). - unittests/displaytest.c: eight attribute-area index expressions using the literal 6144 become DISPLAY_PIXEL_BYTES (available via the existing machine.h → display.h include chain). No behaviour change; the constants evaluate to the same values at compile time.
scalerexpandtest.c previously only exercised the PAL-TV expand_full_width_and_1_line expander. Three other expander functions are used by the remaining scalers but had no coverage: - expand_1 (AdvMAME 2x/3x, HQ 2x/3x/4x): expand one pixel in every direction, then clip. New test scalerexpandtest_advmame covers interior, top-left corner, and bottom-right corner cases. - expand_sai (2xSaI, Super 2xSaI, SuperEagle): expand 2 pixels up/left and 1 pixel down/right, then clip. New test scalerexpandtest_sai covers interior, top-left corner, and bottom-right corner cases. - expand_dotmatrix (Dot Matrix): rounds the dirty rect to an even y co-ordinate. New test scalerexpandtest_dotmatrix checks both the odd-y (rounds down, h increases) and even-y (no change) paths. A fourth test scalerexpandtest_ntsctv verifies that the NTSC TV scalers carry SCALER_FLAGS_FULL_REFRESH and return a NULL expander, confirming that the dispatch code would never call a non-existent expander for them.
Three error-reporting paths passed wrong values to strerror(): 1. putenv() failure (line 72): error holds putenv()'s return value (-1), but putenv() sets errno on failure. The correct call is strerror( errno ). 2. sfifo_init() failure (line 142): sfifo uses the Linux-style convention of returning negative errno values (-EINVAL, -ENOMEM, ...). strerror( error ) therefore received a large negative number and produced 'Unknown error -N'. The correct call is strerror( -error ). 3. sfifo_write() failure (line 184): same negative-errno convention. strerror( i ) should be strerror( -i ). All three fixes make the error messages shown to the user accurate when SDL sound initialisation or output fails.
which supports BASIC tokens and symbols.
and update libspectrum dependency to make sure it is available.
Integrate the Fuse 1.9.1 release and its required libspectrum 1.6.3 update, together with the fork-side changes the upgrade makes necessary: the FuseGenerator and FuseImporter submodule bumps, the composite scaler source added to the Xcode project, the version and release-suffix strings, and the Cocoa filter labels realigned with the renumbered scaler enum.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fuse 1.9.0 and 1.9.1 have been released since the last merge (source).