OXYGENHUB

audio: work out the sample clock ratios once, not after every instruction 9a6dd8288e68

audio_step() runs after every emulated instruction. It used the
*_SAMP_CLKS_PER_CPU_CLK macros, which divide by MHZ, a variable, so each
call did several 64-bit divisions at run time and then divided by the
result as well. They are constants for the run: audio_init() now works
them out once (MHZ is set by then), and the per-instruction division is
replaced by a multiply and compare, dividing only when the YM buffer is
nearly full. min(clocks, room / ratio) is clocks exactly when
clocks * ratio <= room, so the results are the same integers.

perf on narvi (riscv64) put audio.c:242-247 at about 13% of the run
time at the BASIC prompt; integer division is slow there. Measured with
the emulator stepping 600 frames, real-time mode, SDL dummy drivers
(CPU seconds per emulated second, median of 3; % of a real X16):

    narvi  idle  2.406 -> 2.183  (42% -> 46%)
    narvi  busy  1.974 -> 1.914  (51% -> 52%)
    x86    idle  0.546 -> 0.393
    x86    busy  0.574 -> 0.465

Emulation is unchanged: from power-on with -zeroram -nohostfs, three
BASIC workloads (VERA/VIA timing samples, bitmap LINE drawing, FM and
PSG notes), each with sound on and off, give the same registers, RAM
(low and all 64 banks) and screen at frame 900 as before, on x86 and
on riscv64.
Author
Andy McCall @andymccall <[email protected]>
via
agent agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 3d452845da48
When
· 2026-10-21 01:02 UTC
Signature
◈ sealed by agent:claude@andymccall
Hash
9a6dd8288e688b1c07d0291a355ac1ccee4ba62e289bd7992b925997dad453d3
Depends on
03a3459e6372
Depended on by
nothing yet
In bookmarks
main riscv-1
Edits
4 atoms, 0 tree operations, 1 file

Browse the tree at this change

modified src/audio.c +16 −5
…
85 unchanged lines
86 86
static uint32_t vera_samps_per_host_samps = 0;
87 87
static uint32_t ym_samps_per_host_samps = 0;
88 88
static uint32_t fs_samps_per_host_samps = 0;
89 +
// The *_SAMP_CLKS_PER_CPU_CLK ratios, worked out once in audio_init():
90 +
// MHZ is a variable, so the macros divide at run time, and audio_step()
91 +
// runs after every instruction.
92 +
static uint64_t vera_samp_clks_per_cpu_clk = 0;
93 +
static uint64_t ym_samp_clks_per_cpu_clk = 0;
94 +
static uint64_t fs_samp_clks_per_cpu_clk = 0;
89 95
static uint32_t limiter_amp = 0;
90 96
91 97
static int16_t psg_buf[2 * SAMPLES_PER_BUFFER];
…
100 unchanged lines
192 198
	vera_samps_per_host_samps = ((25000000ULL << SAMP_POS_FRAC_BITS) / 512 / host_sample_rate);
193 199
	ym_samps_per_host_samps = ((3579545ULL << SAMP_POS_FRAC_BITS) / 64 / host_sample_rate);
194 200
	fs_samps_per_host_samps = vera_samps_per_host_samps;
201 +
	vera_samp_clks_per_cpu_clk = VERA_SAMP_CLKS_PER_CPU_CLK;
202 +
	ym_samp_clks_per_cpu_clk = YM_SAMP_CLKS_PER_CPU_CLK;
203 +
	fs_samp_clks_per_cpu_clk = FS_SAMP_CLKS_PER_CPU_CLK;
195 204
	vera_samp_pos_rd = 0;
196 205
	vera_samp_pos_wr = 0;
197 206
	vera_samp_pos_hd = 0;
…
41 unchanged lines
239 248
240 249
	while (cpu_clocks > 0) {
241 250
		// Only the source with the higest sample rate (YM2151) is needed for calculation
242 -
		uint32_t max_cpu_clks_ym = ((ym_samp_pos_rd - ym_samp_pos_hd - (1 << SAMP_POS_FRAC_BITS)) & SAMP_POS_MASK_FRAC) / YM_SAMP_CLKS_PER_CPU_CLK;
243 -
		uint32_t max_cpu_clks = SDL_min(cpu_clocks, max_cpu_clks_ym);
244 -
		vera_samp_pos_hd = (vera_samp_pos_hd + max_cpu_clks * VERA_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC;
245 -
		ym_samp_pos_hd = (ym_samp_pos_hd + max_cpu_clks * YM_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC;
246 -
		fs_samp_pos_hd = (fs_samp_pos_hd + max_cpu_clks * FS_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC;
251 +
		uint32_t ym_room = (ym_samp_pos_rd - ym_samp_pos_hd - (1 << SAMP_POS_FRAC_BITS)) & SAMP_POS_MASK_FRAC;
252 +
		// min(cpu_clocks, ym_room / ratio), dividing only when the buffer
253 +
		// is nearly full rather than after every instruction
254 +
		uint32_t max_cpu_clks = (uint64_t)cpu_clocks * ym_samp_clks_per_cpu_clk <= ym_room ? (uint32_t)cpu_clocks : (uint32_t)(ym_room / ym_samp_clks_per_cpu_clk);
255 +
		vera_samp_pos_hd = (vera_samp_pos_hd + max_cpu_clks * vera_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC;
256 +
		ym_samp_pos_hd = (ym_samp_pos_hd + max_cpu_clks * ym_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC;
257 +
		fs_samp_pos_hd = (fs_samp_pos_hd + max_cpu_clks * fs_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC;
247 258
		cpu_clocks -= max_cpu_clks;
248 259
		if (cpu_clocks > 0) audio_render();
249 260
	}
…
202 unchanged lines