65816-llvm-mos/tests/memBlock/memBlockIrq.c
2026-10-06 17:01:51 -05:00

122 lines
4.2 KiB
C

// MVN / MVP stay correct when an interrupt lands mid-move and its handler
// moves memory too. memBlockIrq.lua injects a native-mode interrupt (to
// irqEntry, memBlockIrqEntry.s) whenever the CPU is executing an armed
// outer call's block-move stub on the stack; the handler runs irqNested,
// whose memcpy / memmove / memset use other banks and their own stubs, and
// after RTI the outer move resumes. A move whose bank operands lived in
// shared code would resume with the handler's banks.
// Mailbox: 0x025000 = bad outer results, 0x025002 = bad nested results,
// 0x025004 = 0xC0DE done, 0x025006 = 1 when at least MIN_SHOTS interrupts
// landed mid-move and every handler finished, 0x025020 = irqEntry (set
// here), 0x025022 = armed, 0x025024 = interrupts injected (Lua), 0x025026
// = handlers completed, 0x025028 = a handler is running (Lua sets it, the
// handler clears it).
#include "memPattern.h"
#define MB_BAD (*(volatile u16 *)0x025000UL)
#define MB_BAD_IN (*(volatile u16 *)0x025002UL)
#define MB_DONE (*(volatile u16 *)0x025004UL)
#define MB_SHOT_OK (*(volatile u16 *)0x025006UL)
#define MB_IRQ (*(volatile u16 *)0x025020UL)
#define MB_ARM (*(volatile u16 *)0x025022UL)
#define MB_SHOTS (*(volatile u16 *)0x025024UL)
#define MB_HANDLED (*(volatile u16 *)0x025026UL)
#define MB_BUSY (*(volatile u16 *)0x025028UL)
#define MB_DONE_MARK 0xC0DEu
#define ROUNDS 2u
#define MIN_SHOTS 8u
#define OUTER_LEN 20000UL
#define INNER_LEN 300UL
#define SEED_OUT 0x3Cu
#define SEED_IN 0x96u
#define MOVE_SHIFT 0x123UL
#define IN_SET_VAL 0xA7
// Outer: one MVN (bank 6 to 7), several runs (the source across 08/09, the
// destination across 0A/0B), a backward overlapping MVP across 0C/0D.
#define OUT_SRC1 0x061000UL
#define OUT_DST1 0x072000UL
#define OUT_SRC2 (0x090000UL - 11111UL)
#define OUT_DST2 (0x0B0000UL - 7777UL)
#define OUT_MOVE (0x0D0000UL - 15000UL)
// Inner: banks 0E-11, apart from the outer ones.
#define IN_SRC (0x0F0000UL - 500UL)
#define IN_DST 0x0E4000UL
#define IN_MOVE (0x110000UL - 700UL)
#define IN_SET 0x103000UL
static int filledWith(u32 a, u32 n, u8 c);
void irqEntry(void);
void irqNested(void);
static u16 gBadIn;
static int filledWith(u32 a, u32 n, u8 c) {
const u8 *p = (const u8 *)a;
u16 i;
for (i = 0; i < (u16)n; i++) {
if (p[i] != c) {
return 0;
}
}
return 1;
}
// Called from irqEntry, on the interrupted program's stack.
void irqNested(void) {
fill(IN_SRC, INNER_LEN, SEED_IN);
memset((void *)IN_DST, 0, INNER_LEN);
if (memcpy((void *)IN_DST, (const void *)IN_SRC, INNER_LEN) != (void *)IN_DST ||
!holds(IN_DST, IN_SRC, INNER_LEN, SEED_IN)) {
gBadIn++;
}
fill(IN_MOVE, INNER_LEN + MOVE_SHIFT, SEED_IN);
memmove((void *)(IN_MOVE + MOVE_SHIFT), (const void *)IN_MOVE, INNER_LEN);
if (!holds(IN_MOVE + MOVE_SHIFT, IN_MOVE, INNER_LEN, SEED_IN)) {
gBadIn++;
}
memset((void *)IN_SET, IN_SET_VAL, INNER_LEN);
if (!filledWith(IN_SET, INNER_LEN, (u8)IN_SET_VAL)) {
gBadIn++;
}
MB_BAD_IN = gBadIn;
MB_HANDLED = (u16)(MB_HANDLED + 1u);
MB_BUSY = 0u;
}
int main(void) {
u16 bad = 0u;
u16 r;
MB_IRQ = (u16)(u32)irqEntry;
fill(OUT_SRC1, OUTER_LEN, SEED_OUT);
fill(OUT_SRC2, OUTER_LEN, SEED_OUT);
for (r = 0; r < ROUNDS; r++) {
memset((void *)OUT_DST1, 0, OUTER_LEN);
memset((void *)OUT_DST2, 0, OUTER_LEN);
fill(OUT_MOVE, OUTER_LEN + MOVE_SHIFT, SEED_OUT);
MB_ARM = 1u;
memcpy((void *)OUT_DST1, (const void *)OUT_SRC1, OUTER_LEN);
memcpy((void *)OUT_DST2, (const void *)OUT_SRC2, OUTER_LEN);
memmove((void *)(OUT_MOVE + MOVE_SHIFT), (const void *)OUT_MOVE, OUTER_LEN);
MB_ARM = 0u;
if (!holds(OUT_DST1, OUT_SRC1, OUTER_LEN, SEED_OUT)) {
bad++;
}
if (!holds(OUT_DST2, OUT_SRC2, OUTER_LEN, SEED_OUT)) {
bad++;
}
if (!holds(OUT_MOVE + MOVE_SHIFT, OUT_MOVE, OUTER_LEN, SEED_OUT)) {
bad++;
}
MB_BAD = bad;
}
MB_SHOT_OK = (u16)(MB_SHOTS >= MIN_SHOTS && MB_HANDLED == MB_SHOTS);
MB_DONE = MB_DONE_MARK;
for (;;) {
}
return 0;
}