// 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; }