#!/usr/bin/env python3 # Emits the C source of the smoke check "constant shifts and i32 +/- 1", # with kExp holding the expected results computed here. V32 = [0x00000000, 0xFFFFFFFF, 0x80000000, 0x7FFFFFFF, 0x12345678, 0xFEDCBA98, 0x00018001, 0x8000FFFF] V16 = [0x0000, 0xFFFF, 0x8000, 0x7FFF, 0x1234, 0xFEDC, 0x8001, 0x4000] # (start, count) for the down-counting loops: each decrements a u32 `count` # times from `start` and returns the final value's halves and a checksum. DOWN = [(0x00010002, 5), (0x00000002, 4), (0x00020000, 1), (0x80000001, 3), (0x00000000, 1), (0x00030001, 2)] UP = [(0x0000FFFE, 5), (0xFFFFFFFE, 4), (0x0001FFFF, 1), (0x7FFFFFFF, 2)] def s32(x): x &= 0xFFFFFFFF return x - (1 << 32) if x & 0x80000000 else x def s16(x): x &= 0xFFFF return x - (1 << 16) if x & 0x8000 else x exp = [] for op in ("shl", "shr", "sar"): for n in range(1, 32): for v in V32: if op == "shl": r = (v << n) & 0xFFFFFFFF elif op == "shr": r = v >> n else: r = (s32(v) >> n) & 0xFFFFFFFF exp += [r & 0xFFFF, r >> 16] for n in range(1, 16): for v in V16: exp.append((s16(v) >> n) & 0xFFFF) # Down loops: final value and the sum (mod 2^16) of every low half seen. for start, cnt in DOWN: v = start s = 0 for _ in range(cnt): v = (v - 1) & 0xFFFFFFFF s = (s + (v & 0xFFFF) + (v >> 16)) & 0xFFFF exp += [v & 0xFFFF, v >> 16, s] for start, cnt in UP: v = start s = 0 for _ in range(cnt): v = (v + 1) & 0xFFFFFFFF s = (s + (v & 0xFFFF) + (v >> 16)) & 0xFFFF exp += [v & 0xFFFF, v >> 16, s] def rows(vals, per, fmt): out = [] for i in range(0, len(vals), per): out.append(" " + ", ".join(fmt % x for x in vals[i:i + per]) + ",") return "\n".join(out) print("""// Every constant i32 shift (<< >>u >>s by 1..31) on 8 values, every // constant i16 >>s (1..15) on 8 values, and in-place i32 -= 1 / += 1 loop // counters across 0x0000 / 0x10000 / 0x80000000 / 0xFFFFFFFF. kExp is // computed by genShiftDec.py. Mailbox: 0x025000 = mismatches, 0x025002 = // first failing index, 0x025004 = 0xC0DE done. int is 16 bits, long 32. typedef unsigned short u16; typedef short s16; typedef unsigned long u32; typedef long s32; #define NOI __attribute__((noinline)) static #define NV 8 static const u32 kV32[NV] = { %s }; static const u16 kV16[NV] = { %s };""" % (", ".join("0x%08XUL" % v for v in V32), ", ".join("0x%04Xu" % v for v in V16))) print("static const u16 kExp[%d] = {\n%s\n};" % (len(exp), rows(exp, 12, "0x%04X"))) print(r"""#define SH(n) \ NOI u32 shl##n(u32 x) { return x << n; } \ NOI u32 shr##n(u32 x) { return x >> n; } \ NOI u32 sar##n(u32 x) { return (u32)((s32)x >> n); } #define L7(M, a, b, c, d, e, f, g) M(a) M(b) M(c) M(d) M(e) M(f) M(g) #define L8(M, a, b, c, d, e, f, g, h) L7(M, a, b, c, d, e, f, g) M(h) L8(SH, 1, 2, 3, 4, 5, 6, 7, 8) L8(SH, 9, 10, 11, 12, 13, 14, 15, 16) L8(SH, 17, 18, 19, 20, 21, 22, 23, 24) L7(SH, 25, 26, 27, 28, 29, 30, 31) #define S16(n) NOI u16 sa16_##n(u16 x) { return (u16)((s16)x >> n); } L8(S16, 1, 2, 3, 4, 5, 6, 7, 8) L7(S16, 9, 10, 11, 12, 13, 14, 15) typedef u32 (*Fn32)(u32); typedef u16 (*Fn16)(u16); #define P31(p) p##1, p##2, p##3, p##4, p##5, p##6, p##7, p##8, p##9, p##10, \ p##11, p##12, p##13, p##14, p##15, p##16, p##17, p##18, p##19, p##20, \ p##21, p##22, p##23, p##24, p##25, p##26, p##27, p##28, p##29, p##30, p##31 static Fn32 const kFn32[93] = { P31(shl), P31(shr), P31(sar) }; static Fn16 const kFn16[15] = { sa16_1, sa16_2, sa16_3, sa16_4, sa16_5, sa16_6, sa16_7, sa16_8, sa16_9, sa16_10, sa16_11, sa16_12, sa16_13, sa16_14, sa16_15 }; // The counter lives in a frame / direct-page slot across the loop, where // W65816I32IncFold turns its -= 1 / += 1 into the conditional-carry form. static volatile u16 gSink; NOI u32 down(u32 v, u16 n, u16 *sum) { u16 s = 0; while (n != 0u) { v--; s = (u16)(s + (u16)v + (u16)(v >> 16)); gSink = s; n--; } *sum = s; return v; } NOI u32 up(u32 v, u16 n, u16 *sum) { u16 s = 0; while (n != 0u) { v++; s = (u16)(s + (u16)v + (u16)(v >> 16)); gSink = s; n--; } *sum = s; return v; }""") print("static const u32 kDownStart[%d] = { %s };" % (len(DOWN), ", ".join("0x%08XUL" % d[0] for d in DOWN))) print("static const u16 kDownCount[%d] = { %s };" % (len(DOWN), ", ".join("%du" % d[1] for d in DOWN))) print("static const u32 kUpStart[%d] = { %s };" % (len(UP), ", ".join("0x%08XUL" % d[0] for d in UP))) print("static const u16 kUpCount[%d] = { %s };" % (len(UP), ", ".join("%du" % d[1] for d in UP))) print(r"""static u16 gBad; static u16 gFirst = 0xFFFFu; static u16 gIdx; static void check(u16 got) { if (got != kExp[gIdx] && gBad++ == 0u) { gFirst = gIdx; } gIdx++; } int main(void) { u16 f; u16 i; u16 s; u32 r; for (f = 0u; f < 93u; f++) { for (i = 0u; i < NV; i++) { r = kFn32[f](kV32[i]); check((u16)r); check((u16)(r >> 16)); } } for (f = 0u; f < 15u; f++) { for (i = 0u; i < NV; i++) { check(kFn16[f](kV16[i])); } }""") print(" for (i = 0u; i < %du; i++) {" % len(DOWN)) print(r""" r = down(kDownStart[i], kDownCount[i], &s); check((u16)r); check((u16)(r >> 16)); check(s); }""") print(" for (i = 0u; i < %du; i++) {" % len(UP)) print(r""" r = up(kUpStart[i], kUpCount[i], &s); check((u16)r); check((u16)(r >> 16)); check(s); } *(volatile u16 *)0x025000UL = gBad; *(volatile u16 *)0x025002UL = gFirst; *(volatile u16 *)0x025004UL = 0xC0DEu; for (;;) { } return 0; }""")