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

196 lines
6.1 KiB
C

// memcpy / memmove / memset against a byte-exact model: every length
// class (0, 1, short, both sides of the MVN threshold, long, over 64 KB),
// odd and even source and destination addresses, buffers that straddle a
// bank boundary (source, destination, both), overlapping memmove in both
// directions (short and long, across a bank edge), memset of each class
// and of a value above 255, the returned pointer, and guard bytes on both
// sides; first (memBlockRegs.s) the caller's DBR and D across every path.
// Mailbox: 0x025000 = mismatches, 0x025002 = first failing case (0xFFFF:
// none), 0x025004 = 0xC0DE done, 0x025006 = cases run, 0x025008 = 0x0FFF
// when DBR and D survived all 12 register probes; the first failure's
// dst / src (or c) / n | op << 28 at 0x025010 / 0x025014 / 0x025018.
#include "memPattern.h"
#define MB_BAD (*(volatile u16 *)0x025000UL)
#define MB_FIRST (*(volatile u16 *)0x025002UL)
#define MB_DONE (*(volatile u16 *)0x025004UL)
#define MB_CASES (*(volatile u16 *)0x025006UL)
#define MB_DST (*(volatile u32 *)0x025010UL)
#define MB_SRC (*(volatile u32 *)0x025014UL)
#define MB_N (*(volatile u32 *)0x025018UL)
#define MB_DONE_MARK 0xC0DEu
#define MB_NO_FAIL 0xFFFFu
#define GUARD 8u
#define SEED_SRC 0xC3u
#define SEED_DST 0x5Au
#define SET_BYTE 0xA5
#define SET_BIG 0x3C
#define SET_HIGH 0x100
#define BIG_LEN 70000UL
#define N_LENS 27u
#define N_SHIFTS 10u
#define N_ALIGNS 2u
#define OP_COPY 0x10000000UL
#define OP_MOVE 0x20000000UL
#define OP_SET 0x30000000UL
// Plain buffers in bank 2; bank edges at 03/04, 04/05, 05/06, 06/07 and
// 07/08 for the straddling ones.
#define PLAIN_DST 0x026000UL
#define PLAIN_SRC 0x027000UL
#define PLAIN_MOVE 0x026200UL
#define EDGE_SRC 0x040000UL
#define EDGE_DST 0x050000UL
#define EDGE_MOVE 0x060000UL
#define EDGE_SET 0x070000UL
#define BIG_SRC 0x058000UL
#define BIG_DST 0x074000UL
#define BIG_UP 0x058123UL
#define BIG_DOWN 0x057F00UL
#define BIG_SET 0x09FFFFUL
static void begin(u32 dst, u32 src, u32 n);
static void caseCopy(u32 dst, u32 src, u32 n);
static void caseMove(u32 dst, u32 src, u32 n);
static void caseSet(u32 dst, int c, u32 n);
static void fail(void);
static int guarded(u32 a, u32 n, u8 seed);
static int holdsByte(u32 a, u32 n, u8 c);
u16 memBlockRegs(void);
static const u16 kLens[N_LENS] = {
0, 1, 2, 3, 4, 5, 7, 8, 15, 16, 17, 22, 23, 24, 25, 26, 31, 32, 33, 34,
35, 64, 100, 255, 256, 257, 1000
};
static const short kShifts[N_SHIFTS] = { -100, -7, -3, -2, -1, 1, 2, 3, 7, 100 };
static u16 gBad;
static u16 gCase;
static u32 gDst;
static u32 gSrc;
static u32 gN;
static void begin(u32 dst, u32 src, u32 n) {
gDst = dst;
gSrc = src;
gN = n;
gCase++;
MB_CASES = gCase;
}
static void caseCopy(u32 dst, u32 src, u32 n) {
begin(dst, src, n | OP_COPY);
fill(dst - GUARD, n + 2u * GUARD, SEED_DST);
fill(src, n, SEED_SRC);
if (memcpy((void *)dst, (const void *)src, n) != (void *)dst ||
!holds(dst, src, n, SEED_SRC) || !guarded(dst, n, SEED_DST)) {
fail();
}
}
// One region holds both blocks: after the move [dst, dst + n) holds what
// [src, src + n) held and the rest of the window is untouched.
static void caseMove(u32 dst, u32 src, u32 n) {
u32 lo = (dst < src) ? dst : src;
u32 hi = ((dst < src) ? src : dst) + n;
begin(dst, src, n | OP_MOVE);
fill(lo - GUARD, hi - lo + 2u * GUARD, SEED_SRC);
if (memmove((void *)dst, (const void *)src, n) != (void *)dst ||
!holds(dst, src, n, SEED_SRC) ||
!holds(lo - GUARD, lo - GUARD, dst - (lo - GUARD), SEED_SRC) ||
!holds(dst + n, dst + n, hi + GUARD - (dst + n), SEED_SRC)) {
fail();
}
}
static void caseSet(u32 dst, int c, u32 n) {
begin(dst, (u32)c, n | OP_SET);
fill(dst - GUARD, n + 2u * GUARD, SEED_DST);
if (memset((void *)dst, c, n) != (void *)dst ||
!holdsByte(dst, n, (u8)c) || !guarded(dst, n, SEED_DST)) {
fail();
}
}
static void fail(void) {
if (gBad == 0u) {
MB_FIRST = gCase;
MB_DST = gDst;
MB_SRC = gSrc;
MB_N = gN;
}
gBad++;
MB_BAD = gBad;
}
// The GUARD bytes on each side of [a, a + n) still hold their own pattern.
static int guarded(u32 a, u32 n, u8 seed) {
return holds(a - GUARD, a - GUARD, GUARD, seed) && holds(a + n, a + n, GUARD, seed);
}
static int holdsByte(u32 a, u32 n, u8 c) {
const u8 *p = (const u8 *)a;
while (n != 0u) {
u16 run = (n > 0x8000UL) ? 0x8000u : (u16)n;
u16 i;
for (i = 0; i < run; i++) {
if (p[i] != c) {
return 0;
}
}
p += run;
n -= run;
}
return 1;
}
int main(void) {
u16 li;
u16 d;
u16 s;
u16 k;
// DBR and D survive every path (memBlockRegs.s writes 0x025008 itself).
(void)memBlockRegs();
for (li = 0; li < N_LENS; li++) {
u32 n = kLens[li];
u32 half = n / 2u;
for (d = 0; d < N_ALIGNS; d++) {
for (s = 0; s < N_ALIGNS; s++) {
// Plain, source across 03/04, destination across 04/05,
// both across (at different points of the run).
caseCopy(PLAIN_DST + d, PLAIN_SRC + s, n);
caseCopy(PLAIN_DST + d, EDGE_SRC - half + s, n);
caseCopy(EDGE_DST - half + d, PLAIN_SRC + s, n);
caseCopy(EDGE_DST - half + d, EDGE_SRC - n / 3u + s, n);
}
}
for (k = 0; k < N_SHIFTS; k++) {
caseMove(PLAIN_MOVE + (long)kShifts[k], PLAIN_MOVE, n);
caseMove(EDGE_MOVE - half + (long)kShifts[k], EDGE_MOVE - half, n);
}
caseSet(PLAIN_DST + (li & 1u), SET_BYTE, n);
caseSet(EDGE_SET - half, SET_HIGH + li, n);
}
// Over 64 KB: several MVN / MVP runs, every bank byte stepping.
caseCopy(BIG_DST, BIG_SRC, BIG_LEN);
caseMove(BIG_UP, BIG_SRC, BIG_LEN);
caseMove(BIG_DOWN, BIG_SRC, BIG_LEN);
caseSet(BIG_SET, SET_BIG, BIG_LEN);
MB_BAD = gBad;
if (gBad == 0u) {
MB_FIRST = MB_NO_FAIL;
}
MB_DONE = MB_DONE_MARK;
for (;;) {
}
return 0;
}