| .. | ||
| coremark-src | ||
| build.sh | ||
| core_portme.c | ||
| core_portme.h | ||
| coreMark.manifest.json | ||
| README.md | ||
| runCoreMark.sh | ||
CoreMark — EEMBC's standard embedded benchmark
CoreMark 1.0 ported to the W65816 / Apple IIgs target. Source is
vendored under coremark-src/ from
github.com/eembc/coremark.
CoreMark exercises three distinct algorithm families:
- Linked list traversal + insert/sort (
core_list_join.c) - Matrix init + multiply (
core_matrix.c) - State machine processing a string (
core_state.c)
…plus utility code (CRC, RNG) in core_util.c. Total ~2000 LOC.
This is the embedded benchmark vendors publish CoreMark/MHz scores against (Cortex-M0, AVR, RISC-V, ...). It's a useful cross-platform sanity check on our backend's code-quality.
Files
coremark-src/— vendored EEMBC source (read-only)core_portme.h/.c— W65816 porting layer (timing, malloc, printf bridge)build.sh— compile the 5 core .c files + portmerunCoreMark.sh— build + link + run under MAME
Building
bash tests/coremark/build.sh --layer2
--layer2 enables -mllvm -w65816-dbr-safe-ptrs (smaller, faster
pointer code; correct here because CoreMark only touches malloc/static-
array memory). It is optional: CoreMark plus the runtime it pulls in
(printf, soft-float) no longer fits the ~44 KB below the IO window at
0xC000 either way, so runCoreMark.sh links it multi-segment like the
Lua test (--segment-cap, extra segments at bank 4+) and runs it through
scripts/runMultiSeg.sh.
Default iteration count is 3. CoreMark only validates a run of at least 10 seconds, and one iteration takes about 10.8 s of emulated time (2026-09), so 3 leaves room for the compiler to get faster. Override for longer runs:
ITERATIONS=10 bash tests/coremark/runCoreMark.sh
Running
bash tests/coremark/runCoreMark.sh --layer2
This is a correctness gate, not just a benchmark. core_portme.c sends
CoreMark's console output to $02:6000 and takes its clock from a
millisecond counter at $02:5010 that the MAME harness
(scripts/runMultiSeg.sh) updates every frame; bare metal runs with
IRQs off, so libc's VBL clock() never moves. The script fails unless
CoreMark reports "Correct operation validated" (its own CRC checks plus
the 10-second rule) and the seed, list, matrix, state and final CRCs
match the values a host build of the same sources prints. CoreMark's
own "Total time" and "Iterations/Sec" are the score.
The run ends with 0xC0DE written to $025000 (0xBEEF there means
it started but never finished); elapsed ticks are at $025002.
Note: running CoreMark under MAME inside this project's restricted
shell crashes MAME (same SIGSEGV as Lua's full interpreter run —
see feedback_lua_compile_test.md). The build produces a valid
binary; the run only works in an unrestricted shell. Workaround: copy
coreMark.bin out of the sandbox and run with the same
runInMame.sh invocation directly.
Size vs Calypsi (5 core files, ITERATIONS=1, PERFORMANCE_RUN)
| File | Ours (L2+threshold=50) | Calypsi 5.16 | Ratio |
|---|---|---|---|
| core_list_join.o | 10,008 | 9,073 | 1.10× |
| core_main.o | 11,588 | 19,772 | 0.59× |
| core_matrix.o | 10,660 | 11,078 | 0.96× |
| core_state.o | 7,256 | 9,944 | 0.73× |
| core_util.o | 3,156 | 4,631 | 0.68× |
| TOTAL | 42,668 | 54,498 | 0.78× |
We beat Calypsi by 22% on CoreMark overall. (Since the inline-
threshold dropped from 75 to 50 target-wide, core_matrix.o improved
from 1.37× → 0.96× by no longer inlining 5 nested-loop helpers.)
Notes on the porting layer
ee_u32isunsigned long(notunsigned int— on W65816intis 16-bit;longis 32-bit). CoreMark depends on 32-bitee_u32for CRC and timing math.MEM_METHOD = MEM_STATIC— a single 2 KB static array in BSS. Avoids dynamic alloc and the resulting heap-management overhead.start_time/stop_timeuseclock()which returns the 60 Hz VBL counter.EE_TICKS_PER_SEC = 60.HAS_FLOAT = 1— CoreMark uses double precision for the score calculation; our soft-double handles it.MULTITHREAD = 1— single-context. The IIgs doesn't have threads.
Comparing builds
Lua and CoreMark together cover roughly disjoint code patterns:
| Pattern | Lua | CoreMark |
|---|---|---|
| VM dispatch | yes (luaV_execute 30+ case switch) |
no |
| Recursive descent parsing | yes (lparser.c) |
no |
| String + hash table | yes | no |
| Linked-list traversal + sort | (small) | yes |
| Matrix init + multiply | no | yes |
| State machine | (JSON tokenizer in smoke) | yes (formal CoreMark state) |
| CRC | yes (in smoke) | yes |
| Recursion-heavy | yes | no |
So they complement each other for backend coverage. Both now compile to under-or-near Calypsi size with the standard Layer 2 + threshold=75 config.