Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ test/lldb_*.py
/test/tools/test_blitter_compare
/test/tools/test_screenshot
test/tools/build/

# Acid-test build outputs
test/acid/acid_run
test/acid/tests/**/*.jag
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ test/tools/test_memory_map: test/tools/test_memory_map.c
-o $@ test/tools/test_memory_map.c -ldl
endif

.PHONY: clean test lint coverage benchmark
.PHONY: clean test lint coverage benchmark acid
endif

lint:
Expand Down Expand Up @@ -905,6 +905,18 @@ benchmark:
--warmup $(BENCH_WARMUP) --blitter $(BENCH_BLITTER) \
$(if $(BENCH_STATE),--load-state "$(BENCH_STATE)")

# `make acid` -- builds the core and runs the synthetic acid-test ROMs
# (see test/acid/README.md). Requires the vasm 68K assembler on $PATH;
# if absent, the assemble step is skipped and only the runner harness
# is built (so CI can still validate the harness compiles).
#
# Forces a BENCH_PROFILE=1 + TEST_EXPORTS=1 build of the core so the
# acid runner can dlsym `perf_counters_find` and report a per-test
# delta (halflines, vblank IRQs, blits, inner-loop iters, ...).
acid:
$(MAKE) BENCH_PROFILE=1 TEST_EXPORTS=1 -j$(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)
$(MAKE) -C test/acid test CORE=$(abspath $(TARGET))

print-%:
@echo '$*=$($*)'

32 changes: 32 additions & 0 deletions docs/emulation-bug-hunt-todos.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,35 @@ shipping v2.2.0; capture them so they don't get lost.
/ `const`-correctness audits as a CI step. `clang-tidy` and
`cppcheck` would be good starting points; the codebase already
has a C89 lint, so the infrastructure is there.

## Original `docs/TODO` items still relevant (Shamus / CJ)

The historical `docs/TODO` from the upstream Virtual Jaguar tree
lists several still-open accuracy / feature items. These map onto
the acid-test categories in `test/acid/README.md`; tracking here so
they don't get lost:

- **"Fix VC behavior to match what a real Jaguar does. Still not
sure just what the heck is going on there." [Shamus]**
acid `timing/`. Active suspect for the Doom 1.5-2x speed
regression (issue #131).
- **"Cycle accuracy for GPU/DSP/OP/Blitter." [Shamus]**
cross-cutting; informs every category in `test/acid/`, especially
`bus/` (which can't pass without it).
- **"Need to propagate blitter fixes in the A1 <- A2 direction
to the A1 -> A2 direction and the GPU fixes to various
instructions to the DSP." [Shamus]** — acid `blitter/` (A1↔A2
symmetry tests) and `gpu/` + `dsp/` (shared opcode coverage).
- **"Blitter needs fixing." [Shamus]** — acid `blitter/`.
PR #129 fixed a perf-relevant chunk (`ADDARRAY` etc); accuracy
axis still wide open.
- **"Need to emulate bus contention." [Shamus]** — acid `bus/`.
Almost certainly load-bearing for the Doom regression and the
AvP audio dropouts.
- **"Need to fix timing in the OP. As it is now, it gives a false
impression of how much it's capable of." [Shamus]**
acid `op/`.

The original `docs/TODO` is intentionally left untouched — it's
the authors' historical record and we track our own work via
GitHub issues + this file + `test/acid/`.
18 changes: 17 additions & 1 deletion src/core/jaguar.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "jaguar.h"

#include "cdrom.h"
#include "perf_counters.h"
#include "dac.h"
#include "dsp.h"
#include "eeprom.h"
Expand All @@ -33,6 +34,16 @@

static bool frameDone;

/* Frame-pacing instrumentation (no-op unless built with BENCH_PROFILE).
* Lets the acid runner / benchmark detect timing regressions like the
* Doom 2x speed bug -- e.g. expected 525 halflines/frame NTSC, 60 vblank
* IRQs/sec. See test/acid/README.md and src/core/perf_counters.h.
* Counters that fire from other TUs are declared at their use sites
* (PERF_COUNTER backs each name with a file-scope static). */
PERF_COUNTER(timing_halfline_callbacks);
PERF_COUNTER(timing_vblank_irqs);
PERF_COUNTER(timing_jaguar_execute_calls);

// Platform-independent xorshift32 PRNG for deterministic RAM initialization.
// libc rand() produces different sequences on different platforms (glibc vs
// macOS libsystem), which causes cross-platform baseline mismatches.
Expand Down Expand Up @@ -694,7 +705,8 @@ void JaguarInit(void)
// Half line times are, naturally, half of this. :-P
void HalflineCallback(void)
{
uint16_t vc = TOMReadWord(0xF00006, JAGUAR);
uint16_t vc = (PERF_INC(timing_halfline_callbacks),
TOMReadWord(0xF00006, JAGUAR));
uint16_t vp = TOMReadWord(0xF0003E, JAGUAR) + 1;
uint16_t vi = TOMReadWord(0xF0004E, JAGUAR);

Expand All @@ -712,7 +724,10 @@ void HalflineCallback(void)

// Time for Vertical Interrupt?
if ((vc & 0x7FF) == vi && (vc & 0x7FF) > 0)
{
PERF_INC(timing_vblank_irqs);
TOMSetPendingVideoInt();
}

TOMExecHalfline(vc, true);

Expand Down Expand Up @@ -934,6 +949,7 @@ uint8_t * GetRamPtr(void)
* so the DSP runs alongside the 68K and GPU, matching real hardware timing. */
void JaguarExecuteNew(void)
{
PERF_INC(timing_jaguar_execute_calls);
frameDone = false;

do
Expand Down
5 changes: 5 additions & 0 deletions src/jerry/jerry.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@
#include "eeprom.h"
#include "event.h"
#include "jaguar.h"
#include "perf_counters.h"

PERF_COUNTER(timing_jerry_irqs);
#include "joystick.h"
#include "m68000/m68kinterface.h"
#include "memtrack.h"
Expand Down Expand Up @@ -250,6 +253,7 @@ void JERRYPIT1Callback(void)
// Not sure, but I think we don't generate another IRQ if one's already going...
// But this seems to work... :-/
jerryPendingInterrupt |= IRQ2_TIMER1;
PERF_INC(timing_jerry_irqs);
m68k_set_irq(2); // Generate 68K IPL 2
}
}
Expand All @@ -266,6 +270,7 @@ void JERRYPIT2Callback(void)
if (jerryInterruptMask & IRQ2_TIMER2) // CPU Timer 2 IRQ
{
jerryPendingInterrupt |= IRQ2_TIMER2;
PERF_INC(timing_jerry_irqs);
m68k_set_irq(2); // Generate 68K IPL 2
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/tom/tom.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,11 @@
#include "jaguar.h"
#include "m68000/m68kinterface.h"
#include "op.h"
#include "perf_counters.h"
#include "settings.h"

PERF_COUNTER(timing_gpu_irqs_to_68k);

// Red Color Values for CrY<->RGB Color Conversion
uint8_t redcv[16][16] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
Expand Down Expand Up @@ -1316,7 +1319,10 @@ void TOMExecPIT(uint32_t cycles)
GPUSetIRQLine(GPUIRQ_TIMER, ASSERT_LINE); // GPUSetIRQLine does the 'IRQ enabled' checking

if (TOMIRQEnabled(IRQ_TIMER))
{
PERF_INC(timing_gpu_irqs_to_68k);
m68k_set_irq(2); // Cause a 68000 IPL 2...
}

TOMResetPIT();
}
Expand All @@ -1329,7 +1335,10 @@ void TOMPITCallback(void)
GPUSetIRQLine(GPUIRQ_TIMER, ASSERT_LINE); // It does the 'IRQ enabled' checking

if (TOMIRQEnabled(IRQ_TIMER))
{
PERF_INC(timing_gpu_irqs_to_68k);
m68k_set_irq(2); // Generate a 68K IPL 2...
}

TOMResetPIT();
}
Expand Down
85 changes: 85 additions & 0 deletions test/acid/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#
# test/acid/Makefile - assembles the synthetic acid-test ROMs.
#
# Toolchain: vasm (motorola syntax) + vlink. Get them from
# http://sun.hasenbraten.de/vasm/ and http://sun.hasenbraten.de/vlink/.
# Build vasm with `make CPU=m68k SYNTAX=mot`.
#
# If `vasmm68k_mot` is not on $PATH this Makefile prints a one-line
# warning and skips the assemble step entirely.
#

SRCDIR := tests
INCDIR := include
RUNNER_BIN := acid_run

VASM ?= vasmm68k_mot

VASM_FLAGS := -Fbin -m68000 -spaces -I$(INCDIR)

SOURCES := $(shell find $(SRCDIR) -name '*.s' -type f 2>/dev/null)
ROMS := $(SOURCES:.s=.jag)

VASM_PRESENT := $(shell command -v $(VASM) 2>/dev/null)

ifeq ($(VASM_PRESENT),)
ROMS_TO_BUILD :=
else
ROMS_TO_BUILD := $(ROMS)
endif

.PHONY: all clean check-vasm test

all: $(RUNNER_BIN) $(ROMS_TO_BUILD)
@$(MAKE) -s check-vasm

check-vasm:
ifeq ($(VASM_PRESENT),)
@echo "** $(VASM) not found on PATH"
@echo "** Skipped assembling acid-test ROMs."
@echo "** See test/acid/README.md for vasm install instructions."
endif

# .s -> .jag: assemble flat binary at the program's org address ($800000),
# then pad to 1 MB so retro_load_game sees a normal-sized cart.
%.jag: %.s
@mkdir -p $(dir $@)
$(VASM) $(VASM_FLAGS) -o $@ $<
@actual=$$(wc -c < $@); \
target=1048576; \
if [ $$actual -lt $$target ]; then \
dd if=/dev/zero bs=1 count=$$(($$target - $$actual)) >> $@ 2>/dev/null; \
fi
@echo " ASM $< -> $@ ($$(wc -c < $@) bytes)"

clean:
rm -f $(ROMS) $(RUNNER_BIN)

# Build the harness (separate from the .jag ROMs themselves).
$(RUNNER_BIN): run.c
$(CC) -O2 -Wall -std=c99 \
-I../../libretro-common/include \
-o $@ $< \
$(if $(filter Linux,$(shell uname -s)),-ldl)

# Run all built tests through the harness. CORE points at the libretro
# core .dylib/.so (defaults to the project root build).
CORE ?= $(firstword $(wildcard ../../virtualjaguar_libretro.dylib ../../virtualjaguar_libretro.so))

test: all
@if [ -z "$(CORE)" ]; then \
echo "ERROR: set CORE=path/to/virtualjaguar_libretro.{dylib,so}"; \
exit 2; \
fi
@if [ -z "$(ROMS_TO_BUILD)" ]; then \
echo "Nothing to run (no .jag ROMs assembled)."; \
exit 0; \
fi
@fail=0; total=0; \
for rom in $(ROMS_TO_BUILD); do \
total=$$((total+1)); \
if ! ./$(RUNNER_BIN) "$(CORE)" "$$rom"; then fail=$$((fail+1)); fi; \
done; \
echo "----"; \
echo "Acid tests: $$((total-fail)) / $$total passed"; \
exit $$fail
Loading
Loading