joeylib2/make/support/iigs/ntpIrqWindow.patch

108 lines
5 KiB
Diff

Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
See LICENSE.md for license information.
SPDX-License-Identifier: MIT
ntpIrqWindow.patch - bound NinjaTrackerPlus's interrupts-off stretches.
make/iigs.mk applies it to the replayer's source with patch --fuzz=0 before
Merlin32 assembles it.
NinjaTrackerPlus moves sample data into the DOC with interrupts masked,
~4 us a byte (measured under MAME), and the Z8530 SCC's receive FIFO only
lasts ~3.1 ms at 9600 baud (~2.3 ms under MAME). Two stretches outlast it:
- NTPstreamsound fills a stream's 512-byte DOC page with two unrolled
256-byte copies under its own SEI: ~2.4 ms. stream_window goes between
the halves: pending interrupts run there, then the DOC's mode and
address are put back for the second half. The stream's oscillators are
already halted, and its job is cleared first, so a refill interrupt left
pending by the slot's previous sound cannot stream into the page.
- The DOC interrupt handler refills every streaming oscillator that is
due before it returns, 256 bytes each: ~1 ms apiece, ~2.5 ms for two.
It returns after one instead; the DOC keeps its IRQ up for the rest, so
the handler is re-entered at once, after anything the ROM's interrupt
manager puts first (the SCC is on its fast path).
Both only while the word at offset 27 of the replayer (irq_window, right
after the documented jump table) is nonzero. The word after it, at offset
29 (playing_addr), holds the address of the replayer's playing flag, so
audio_full.c can see a song that does not loop come to its end. audio_full.c
sets it from hal.c's tight interrupt-latency bound before every
NTPstreamsound, so with the bound loose the replayer does exactly what it
always did.
The source is the installer's pinned ntpsources.zip (checksummed), so every
hunk applies exactly; a hunk that does not means NTP changed and this patch
must be revisited, so the build fails instead of guessing.
--- ninjatrackerplus.s
+++ ninjatrackerplus.s
@@ -138,6 +138,14 @@
; 1 byte channel (0-7 placed in the upper 4 bits, example: $00 for left, $10 for right)
jmp streamsound
+; irq_window (JoeyLib, offset 27): nonzero bounds the interrupts-off
+; stretches -- see stream_window and interrupt_handler_exit.
+irq_window dw 0
+
+; playing_addr (JoeyLib, offset 29): where the replayer keeps its playing
+; flag (0 once a song played once has ended), for jlAudioIsPlayingMod.
+playing_addr dw playing
+
*-------------------------------
; to stay out of the way, do not use the direct page at all
@@ -1301,6 +1309,7 @@
ldy streamsound_osc2
jsr stream_copy ;fill buffer with 512 bytes for first play
+ jsr stream_window ;JoeyLib: interrupts in between the halves, if asked
jsr stream_copy_no_init
mx %00 ;we're in 16bit mode on return
@@ -1383,6 +1392,31 @@
clc
rtl
+; stream_window (JoeyLib): between the two halves of streamsound's first
+; DOC fill, when irq_window is nonzero, let pending interrupts run, then
+; point the DOC back at the second half (a handler may have used it).
+; In and out: mx %00, Y = the stream's track * 2.
+ mx %00
+stream_window = *
+ lda irq_window
+ beq stream_window_1
+ sep #$20
+ mx %10
+ ldx streamsound_osc ;no refill job left pending from the slot's last sound
+ stz oscillator_job_tbl,x
+ cli
+ nop ;a pending interrupt is taken here
+ sei
+ jsr access_doc_ram
+ rep #$20
+ mx %00
+ lda stream_doc_page,y ;the page's second half
+ inc
+ xba
+ sta sound_address
+stream_window_1 = *
+ rts
+
mx %10
streamsound_copy = * ;copy a byte from stream structure to multiple doc registers
ldy streamsound_osc_count
@@ -1454,6 +1488,8 @@
ldx interrupt_job
jsr (interrupt_jump_tbl,x)
+ lda irq_window ;JoeyLib: one oscillator an interrupt when asked;
+ bne interrupt_handler_exit ;the DOC keeps its IRQ up for any others
]loop lda sound_control
bmi ]loop
@@ -1465,6 +1501,7 @@
lda sound_data
bpl interrupt_handler_loop
+interrupt_handler_exit = *
sep #$30
pld
plb