122 lines
4.2 KiB
C
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;
|
|
}
|