From: Mark Wooding Date: Tue, 13 Oct 2020 18:49:43 +0000 (+0100) Subject: Initial version. AMD64 only, and only as far as 0x16. X-Git-Url: https://git.distorted.org.uk/~mdw/xchg-rax-rax/commitdiff_plain/06297a937874bfd892c0c85ace29fb26a4a0ddc0 Initial version. AMD64 only, and only as far as 0x16. --- 06297a937874bfd892c0c85ace29fb26a4a0ddc0 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca069ff --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +### -*-makefile-*- + +V = 0 +vcond = $(call vcond_$V,$1,$2) +vcond_0 = $1 +vcond_1 = $2 +V_AT = $(call vcond,@) +vtag = \ + $(call vcond,@printf " %-8s %s\n" "$1" "$(or $2,$@)";) + +CC = gcc -m64 +CFLAGS = -O2 -g -Wall -Werror + +AS = gcc -m64 +ASFLAGS = -O2 -g + +LD = gcc -m64 +LDFLAGS = + +%.o: %.c + $(call vtag,CC)$(CC) -c -o $@ $(CFLAGS) $< + +%.o: %.S + $(call vtag,AS)$(AS) -c -o $@ $(ASFLAGS) $< + +all:: xchg +xchg: xchg.o main.o + $(call vtag,LD)$(LD) -o $@ $^ + +clean::; rm -f xchg *.o diff --git a/main.c b/main.c new file mode 100644 index 0000000..9ab4c2e --- /dev/null +++ b/main.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include +#include + +union reg { + unsigned char *p; + long i; + long u; +}; + +struct regs { + union reg + a, b, c, d, si, di, bp, f, + r8, r9, r10, r11, r12, r13, r14, r15; +}; + +struct seg { + unsigned char *p; + size_t sz; +}; + +#define N(v) (sizeof(v)/sizeof((v)[0])) + +#define CTYPE_HACK(func, ch) func((unsigned char)(ch)) +#define ISDIGIT(ch) CTYPE_HACK(isdigit, ch) +#define ISSPACE(ch) CTYPE_HACK(isspace, ch) + +#define XCHG(_) \ + _(x00) _(x01) _(x02) _(x03) _(x04) _(x05) _(x06) _(x07) \ + _(x08) _(x09) _(x0a) _(x0b) _(x0c) _(x0d) _(x0e) _(x0f) \ + _(x10) _(x11) _(x12) _(x13) _(x14) _(x15) _(x16) _(x17) \ + _(x18) _(x19) _(x1a) _(x1b) _(x1c) _(x1d) _(x1e) _(x1f) \ + _(x20) _(x21) _(x22) _(x23) _(x24) _(x25) _(x26) _(x27) \ + _(x28) _(x29) _(x2a) _(x2b) _(x2c) _(x2d) _(x2e) _(x2f) \ + _(x30) _(x31) _(x32) _(x33) _(x34) _(x35) _(x36) _(x37) \ + _(x38) _(x39) _(x3a) _(x3b) _(x3c) _(x3d) _(x3e) _(x3f) + +#define DECL(x) extern const int x; +XCHG(DECL) +extern const int nop; + +static const int *x[] = { +#define SLOT(x) &x, + XCHG(SLOT) +}; + +extern void call_example(const int *f, struct regs *r); + +static const char *prog = "???"; + +__attribute__((format(printf, 1, 2), noreturn)) +static void barf(const char *m, ...) +{ + va_list ap; + + va_start(ap, m); + fprintf(stderr, "%s: ", prog); + vfprintf(stderr, m, ap); + putc('\n', stderr); + va_end(ap); + exit(127); +} + +static void *xmalloc(size_t sz) +{ + void *p; + + if (!sz) return (0); + p = malloc(sz); + if (!p) barf("malloc failed"); + return (p); +} + +#define DEF_PARSEINT(name, ty, strto) \ + static ty parse_##name(const char *what, const char *p, ty min, ty max) \ + { \ + const char *pp = p; \ + char *q; \ + ty i; \ + int err; \ + \ + if (ISSPACE(*p)) goto bad; \ + err = errno; errno = 0; \ + i = strto(p, &q, 0); \ + if (errno) goto bad; \ + if (*q) goto bad; \ + if (i < min || i > max) goto bad; \ + errno = err; \ + return (i); \ + \ + bad: \ + barf("bad %s `%s'", what, pp); \ + } +DEF_PARSEINT(long, long, strtol) +DEF_PARSEINT(ulong, unsigned long, strtoul) + +static int hex_digit(char ch) +{ + if ('0' <= ch && ch <= '9') return (ch - '0'); + else if ('A' <= ch && ch <= 'F') return (ch - 'A' + 10); + else if ('a' <= ch && ch <= 'f') return (ch - 'a' + 10); + else return (-1); +} + +static void setreg(union reg *r, + struct seg **seg_inout, + int *i_inout, int argc, char *argv[]) +{ + struct seg *seg; + const char *p, *pp; + unsigned char *q; + int hi, lo; + size_t n; + +#define LONG_REG(p) (parse_long("signed register", (p), LONG_MIN, LONG_MAX)) +#define ULONG_REG(p) (parse_ulong("unsigned register", (p), 0, ULONG_MAX)) + + p = *i_inout >= argc ? "-" : argv[(*i_inout)++]; + switch (*p) { + case '-': + if (p[1]) r->i = LONG_REG(p); + else r->u = 0xdeadbeefdeadbeef; + break; + case 'i': + if (p[1] != ':') goto bad; + r->i = LONG_REG(p + 2); + break; + case 'u': + if (p[1] != ':') goto bad; + r->u = ULONG_REG(p + 2); + break; + case 'c': + if (p[1] != ':' || p[3]) goto bad; + r->u = p[2]; + break; + case 's': + if (p[1] != ':') goto bad; + pp = p + 2; n = strlen(pp) + 1; + seg = (*seg_inout)++; seg->p = xmalloc(n); seg->sz = n; + memcpy(seg->p, pp, n); r->p = seg->p; + break; + case 'm': + if (p[1] != ':') goto bad; + pp = p + 2; n = strlen(pp); if (n%2) goto bad; + seg = (*seg_inout)++; seg->p = q = xmalloc(n/2); seg->sz = n/2; + while (n) { + hi = hex_digit(pp[0]); lo = hex_digit(pp[1]); + if (hi < 0 || lo < 0) goto bad; + *q++ = 16*hi + lo; n -= 2; pp += 2; + } + r->p = seg->p; + break; + default: + if (ISDIGIT(*p)) r->u = ULONG_REG(p); + else if (*p == '+') r->i = LONG_REG(p); + else if (*p == '\'' && p[2] == '\'' && !p[3]) r->u = p[1]; + else goto bad; + break; + bad: + barf("bad regspec `%s'", p); + } + +#undef LONG_REG +#undef ULONG_REG +} + +static void dumpreg(const char *name, const union reg *r, + const struct seg *seg, size_t nseg) +{ + size_t i; + + printf("%3s = 0x%016lx = %20ld = %20lu", name, r->u, r->i, r->u); + if (r->u >= ' ' && r->u <= '~') printf(" = '%c'", (int)r->u); + for (i = 0; i < nseg; i++) { + if (r->p == seg[i].p) + printf(" = seg[%zu] base", i); + else if (r->p == seg[i].p + seg[i].sz) + printf(" = seg[%zu] limit", i); + else if (seg[i].p < r->p && r->p < seg[i].p + seg[i].sz) + printf(" = seg[%zu] + %zu", i, (size_t)(r->p - seg[i].p)); + } + putchar('\n'); +} + +static void dumpseg(const struct seg *seg) +{ + size_t i, j; + unsigned char ch; + + for (i = 0; i < seg->sz; i += 8) { + printf("\t%8zx :", i); + for (j = 0; j < 8; j++) + if (i + j >= seg->sz) printf(" **"); + else printf(" %02x", seg->p[i + j]); + printf(" : "); + for (j = 0; j < 8; j++) + if (i + j >= seg->sz) putchar('*'); + else { + ch = seg->p[i + j]; + if (' ' <= ch && ch <= '~') putchar(ch); + else putchar('.'); + } + putchar('\n'); + } +} + +int main(int argc, char *argv[]) +{ + struct regs r; + struct seg seg[16], *segp = seg; + size_t nseg; + int i, j; + + prog = strrchr(argv[0], '/'); if (prog) prog++; else prog = argv[0]; + + if (argc < 2) + barf("usage: %s I [A B C D SI DI BP R8 R9 R10 R11 R12 R13 R14 R15 F]", + prog); + + j = parse_long("program index", argv[1], -1, N(x) - 1); + + i = 2; + setreg(&r.a, &segp, &i, argc, argv); + setreg(&r.b, &segp, &i, argc, argv); + setreg(&r.c, &segp, &i, argc, argv); + setreg(&r.d, &segp, &i, argc, argv); + setreg(&r.si, &segp, &i, argc, argv); + setreg(&r.di, &segp, &i, argc, argv); + setreg(&r.bp, &segp, &i, argc, argv); + setreg(&r.r8, &segp, &i, argc, argv); + setreg(&r.r9, &segp, &i, argc, argv); + setreg(&r.r10, &segp, &i, argc, argv); + setreg(&r.r11, &segp, &i, argc, argv); + setreg(&r.r12, &segp, &i, argc, argv); + setreg(&r.r13, &segp, &i, argc, argv); + setreg(&r.r14, &segp, &i, argc, argv); + setreg(&r.r15, &segp, &i, argc, argv); + setreg(&r.f, &segp, &i, argc, argv); + nseg = segp - seg; + + call_example(j < 0 ? &nop : x[j], &r); + + dumpreg("rax", &r.a, seg, nseg); + dumpreg("rbx", &r.b, seg, nseg); + dumpreg("rcx", &r.c, seg, nseg); + dumpreg("rdx", &r.d, seg, nseg); + dumpreg("rsi", &r.si, seg, nseg); + dumpreg("rdi", &r.di, seg, nseg); + dumpreg("rbp", &r.bp, seg, nseg); + dumpreg("rbp", &r.bp, seg, nseg); + dumpreg("r8", &r.r8, seg, nseg); + dumpreg("r9", &r.r9, seg, nseg); + dumpreg("r10", &r.r10, seg, nseg); + dumpreg("r11", &r.r11, seg, nseg); + dumpreg("r12", &r.r12, seg, nseg); + dumpreg("r13", &r.r13, seg, nseg); + dumpreg("r14", &r.r14, seg, nseg); + dumpreg("r15", &r.r15, seg, nseg); + +#define CF (1 << 0) +#define PF (1 << 2) +#define ZF (1 << 6) +#define SF (1 << 7) +#define OF (1 << 11) + + dumpreg("f", &r.f, seg, nseg); + printf("\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n", + (r.f.u >> 0)&1u ? '+' : '-', + (r.f.u >> 2)&1u ? '+' : '-', + (r.f.u >> 4)&1u ? '+' : '-', + (r.f.u >> 6)&1u ? '+' : '-', + (r.f.u >> 7)&1u ? '+' : '-', + (r.f.u >> 10)&1u ? '+' : '-', + (r.f.u >> 11)&1u ? '+' : '-'); + printf("\tcond:"); + if (r.f.u&CF) printf(" c/b/nae"); else printf(" nc/ae/nb"); + if (r.f.u&ZF) printf(" e/z"); else printf(" ne/nz"); + if (r.f.u&SF) printf(" s"); else printf(" ns"); + if (r.f.u&OF) printf(" o"); else printf(" no"); + if (r.f.u&PF) printf(" p"); else printf(" np"); + if ((r.f.u&CF) || (r.f.u&ZF)) printf(" be/na"); else printf(" a/nbe"); + if (!(r.f.u&OF) == !(r.f.u&SF)) printf(" ge/nl"); else printf(" l/nge"); + if (!(r.f.u&OF) == !(r.f.u&SF) && !(r.f.u&ZF)) + printf(" g/nle"); else printf(" le/ng"); + putchar('\n'); + printf("\tsystem: %ctf %cif iopl=%d %cnt " + "%crf %cvm %cac %cvif %cvip %cid\n", + (r.f.u >> 8)&1u ? '+' : '-', + (r.f.u >> 9)&1u ? '+' : '-', + (int)((r.f.u >> 12)&1u), + (r.f.u >> 14)&1u ? '+' : '-', + (r.f.u >> 16)&1u ? '+' : '-', + (r.f.u >> 17)&1u ? '+' : '-', + (r.f.u >> 18)&1u ? '+' : '-', + (r.f.u >> 19)&1u ? '+' : '-', + (r.f.u >> 20)&1u ? '+' : '-', + (r.f.u >> 21)&1u ? '+' : '-'); + +#undef CF +#undef PF +#undef ZF +#undef SF +#undef OF + + for (i = 0; i < nseg; i++) + { printf("seg[%d] (%p):\n", i, seg[i].p); dumpseg(&seg[i]); } + + return (0); +} diff --git a/xchg.S b/xchg.S new file mode 100644 index 0000000..5374727 --- /dev/null +++ b/xchg.S @@ -0,0 +1,745 @@ +/// -*- mode: asm; asm-comment-char: ?/ -*- + + .intel_syntax noprefix + + .section .note.GNU-stack, "", @progbits + +.macro proc name + .globl \name + .type \name, STT_FUNC + .p2align 4 +\name\(): + .macro endproc + .size \name, . - \name + .purgem endproc + .endm +.endm + +.macro ch c + pushf + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push rbp + mov rbp, rsp + and rsp, -16 + + mov rdi, \c + call putchar@plt + + mov rdi, [rip + stdout] + call fflush@plt + + mov rsp, rbp + pop rbp + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popf +.endm + + .text + +proc call_example + + push rbx // rbx + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push rbp // flags, rbp, ..., rbx + pushf + + push rsi // regs, flags, rbp, ..., rbx + + lea rax, [rip + 9f] + push rax // cont, regs, flags, rbp, ..., rbx + push rdi // func, cont, regs, flags, rbp, ..., rbx + + mov rax, [rsi + 56] + pushf + pop rcx + and rax, 0x0cd5 + and rcx, ~0x0cd5 + or rax, rcx + push rax + popf + mov rax, [rsi + 0] + mov rbx, [rsi + 8] + mov rcx, [rsi + 16] + mov rdx, [rsi + 24] + mov rdi, [rsi + 40] + mov rbp, [rsi + 48] + mov rsi, [rsi + 32] + + ret // -> func; regs, flags, rbp, ..., rbx + +9: pushf // rflags, regs, flags, rbp, ..., rbx + push rsi // rsi, rflags, regs, flags, rbp, ..., rbx + mov rsi, [rsp + 16] + mov [rsi + 0], rax + mov [rsi + 8], rbx + mov [rsi + 16], rcx + mov [rsi + 24], rdx + mov [rsi + 40], rdi + mov [rsi + 48], rbp + pop rax // rflags, regs, flags, rbp, ..., rbx + mov [rsi + 32], rax + pop rax // regs, flags, rbp, ..., rbx + mov [rsi + 56], rax + + add rsp, 8 // flags, rbp, ..., rbx + popf // rbp, ..., rbx + pop rbp // ..., rbx + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop rbx // + ret + +endproc + +proc nop + + ret + +endproc + +///-------------------------------------------------------------------------- + +proc x00 + + // clear all 64 bits of extended traditional registers + xor eax,eax // clear rax + lea rbx,[0] // rbx -> _|_ + loop . // iterate, decrement rcx until zero + mov rdx,0 // set rdx = 0 + and esi,0 // clear all bits of rsi + sub edi,edi // set rdi = edi - edi = 0 + push 0 + pop rbp // pop 0 into rbp + + ret + +endproc + +proc x01 + + // advance a fibonacci pair by c steps + // + // on entry, a and d are f_{i+1} and f_i; on exit, they are f_{i+c+1} + // and f_{i+c}, where f_{i+1} = f_i + f_{i-1} +0: xadd rax, rdx // a, d = a + d, a + // = f_{i+1} + f_i, f_{i+1} + // = f_{i+2}, f_{i+1} + loop 0b // advance i, decrement c, iterate + + ret + +endproc + +proc x02 + + // boolean canonify a: if a = 0 on entry, leave it zero; otherwise + // set a = 1 + neg rax // set cf iff a /= 0 + sbb rax, rax // a = a - a - cf = -cf + neg rax // a = cf + + ret + +endproc + +proc x03 + + // set a = min(a, d) (unsigned); clobber c, d + sub rdx, rax // d' = d - a; set cf if a > d + sbb rcx, rcx // c = -cf = -[a > d] + and rcx, rdx // c = a > d ? d - a : 0 + add rax, rcx // a' = a > d ? d : a + + ret + +endproc + +proc x04 + + // switch case? + xor al, 0x20 + + ret + +endproc + +proc x05 + + // answer whether 5 <= a 4 a > 9 or a < 5 + // nc/ae/nb a' >= 4 a >= 9 or a < 5 + // c/b/nae a' < 4 5 <= a < 9 + // be/na a' <= 4 5 <= a <= 9 + // + // o a' < -2^63 + 4 -2^63 + 5 <= a < -2^63 + 9 + // no a' >= -2^63 + 4 a >= -2^63 + 9 or + // a < -2^63 + 5 + // s -2^63 + 4 <= a' < 4 -2^63 + 9 <= a < 9 + // ns a' < -2^63 + 4 or a < -2^63 + 9 or a >= 9 + // a' >= 4 + // ge/nl a' >= 4 a >= 9 or a < -2^63 + 5 + // l/nge a' < 4 -2^63 + 5 <= a < 9 + // g/nle a' > 4 a > 9 or a < -2^63 + 5 + // le/ng a' <= 4 -2^63 + 5 <= a <= 9 + + ret + +endproc + +proc x06 + + // leave a unchanged, but set zf if a = 0, cf if a /= 0, clear of, + // set sf to msb(a) + not rax // a' = -a - 1 + inc rax // a' = -a + neg rax // a' = a + + ret + +endproc + +proc x07 + + // same as before (?) + inc rax // a' = a + 1 + neg rax // a' = -a - 1 + inc rax // a' = -a + neg rax // a' = a + + ret + +endproc + +proc x08 + + // floor((a + d)/2), correctly handling overflow conditions; final cf + // is lsb(a + d), probably uninteresting + add rax, rdx // cf || a' = a + d + rcr rax, 1 // shift 65-bit result right by one + // place; lsb moves into carry + + ret + +endproc + +proc x09 + + // a = a/8, rounded to nearest; i.e., floor(a/8) if a == 0, 1, 2, 3 + // (mod 8), or ceil(a/8) if a == 4, 5, 6, 7 (mod 8). + shr rax, 3 // a' = floor(a/8); cf = 1 if a == + // 4, 5, 6, 7 (mod 8) + adc rax, 0 // a' = floor(a/8) + cf + + ret + +endproc + +proc x0a + + // increment c-byte little-endian bignum at rdi + add byte ptr [rdi], 1 +0: inc rdi + adc byte ptr [rdi], 0 + loop 0b + + ret + +endproc + +proc x0b + + // negate double-precision d:a + not rdx // d' = -d - 1 + neg rax // a' = -a; + // cf = 1 iff a /= 0 + sbb rdx, -1 // d' = -d - cf + + ret + +endproc + +proc x0c + + // rotate is distributive over xor. + + // rax // = a_1 || a_0 + // rbx // = b_1 || b_0 + mov rcx, rax // = a_1 || a_0 + + xor rcx, rbx // = (a_1 XOR b_1) || (a_0 XOR b_0) + ror rcx, 0xd // = (a_0 XOR b_0) || (a_1 XOR b_1) + + ror rax, 0xd // = a_0 || a_1 + ror rbx, 0xd // = b_0 || b_1 + xor rax, rbx // = (a_0 XOR b_0) || (a_1 XOR b_1) + + cmp rax, rcx // always equal + + ret + +endproc + +proc x0d + + // and is distributive over xor. + + mov rdx, rbx // = b + + xor rbx, rcx // = b XOR c + and rbx, rax // = a AND (b XOR c) + + and rdx, rax // = a AND b + and rax, rcx // = a AND c + xor rax, rdx // = (a AND b) XOR (a AND c) + // = a AND (b XOR c) + + cmp rax, rbx // always equal + + ret + +endproc + +proc x0e + + // de morgan's law + + mov rcx, rax // = a + + and rcx, rbx // = a AND b + not rcx // = NOT (a AND b) + + not rax // = NOT a + not rbx // = NOT b + or rax, rbx // = (NOT a) OR (NOT b) + // = NOT (a AND b) + + cmp rax, rcx + + ret + +endproc + +proc x0f + + // replace input buffer bytes with cumulative XORs with initial a; + // final a is XOR of all buffer bytes and initial a. + // + // not sure why you'd do this. + + cld + +0: xor [rsi], al + lodsb + loop 0b + + ret + +endproc + +proc x10 + + // four different ways to swap a pair of registers. + + push rax + push rcx + pop rax + pop rcx + + xor rax, rcx + xor rcx, rax + xor rax, rcx + + add rax, rcx + sub rcx, rax + add rax, rcx + neg rcx + + xchg rax, rcx + + ret + +endproc + +proc x11 + + // assuming a is initialized to zero, set a to the inclusive or of + // the xor-differences of corresponding bytes in the c-byte strings + // at si and di. + // + // in particular, a will be zero (and zf set) if and only if the two + // strings are equal. + +0: mov dl, [rsi] + xor dl, [rdi] + inc rsi + inc rdi + or al, dl + loop 0b + + ret + +endproc + +proc x12 + + // an obtuse way of adding two registers. for any bit position, a + // OR d is set if and only if at least one of a and d has a bit set + // in that position, and a AND d is set if and only if both have a + // bit set in that position. essentially, then, what we've done is + // move all of the set bits in d to a, unless there's already a bit + // there. this clearly doesn't change the sum. + + mov rcx, rdx // c' = d + and rdx, rax // d' = a AND d + or rax, rcx // a' = a OR d + add rax, rdx + + ret + +endproc + +proc x13 + + // ok, so this is a really obtuse way of adding a and b; the result + // is in a and d. but why does it work? + + mov rcx, 0x40 // carry chains at most 64 long +0: mov rdx, rax // copy a' + xor rax, rbx // low bits of each bitwise sum + and rbx, rdx // carry bits from each bitwise sum + shl rbx, 001 // carry them into next position + loop 0b + + ret + +endproc + +proc x14 + + // floor((a + d)/2), like x08. + + mov rcx, rax // copy a for later + and rcx, rdx // carry bits + + xor rax, rdx // low bits of each bitwise sum + shr rax, 1 // divide by 2; carries now in place + + add rax, rcx // add the carries; done + + ret + +endproc + +proc x15 + + // sign extension 32 -> 64 bits. + + //movsx rbx, eax // like this? + + mov rdx, 0xffffffff80000000 + add rax, rdx // if bit 31 of a is set then bits + // 31--63 of a' are clear; otherwise, + // these bits are all set -- which is + // exactly backwards + xor rax, rdx // so fix it + + ret + +endproc + +proc x16 + + shl rax, 56 + shl rbx, 56 + shl rcx, 56 + + xor rax, rbx // a' = a XOR b + xor rbx, rcx // b' = b XOR c + mov rsi, rax // t = a XOR b + add rsi, rbx // t = (a XOR b) + (b XOR c) + cmovc rax, rbx // a' = cf ? b XOR c : a XOR b + xor rax, rbx // a' = cf ? 0 : a XOR c + cmp rax, rsi + + ret + +endproc + +proc x17 + + ud2 + +endproc + +proc x18 + + ud2 + +endproc + +proc x19 + + ud2 + +endproc + +proc x1a + + ud2 + +endproc + +proc x1b + + ud2 + +endproc + +proc x1c + + ud2 + +endproc + +proc x1d + + ud2 + +endproc + +proc x1e + + ud2 + +endproc + +proc x1f + + ud2 + +endproc + +proc x20 + + ud2 + + ret + +endproc + +proc x21 + + ud2 + +endproc + +proc x22 + + ud2 + +endproc + +proc x23 + + ud2 + +endproc + +proc x24 + + ud2 + +endproc + +proc x25 + + ud2 + +endproc + +proc x26 + + ud2 + +endproc + +proc x27 + + ud2 + +endproc + +proc x28 + + ud2 + +endproc + +proc x29 + + ud2 + +endproc + +proc x2a + + ud2 + +endproc + +proc x2b + + ud2 + +endproc + +proc x2c + + ud2 + +endproc + +proc x2d + + ud2 + +endproc + +proc x2e + + ud2 + +endproc + +proc x2f + + ud2 + +endproc + +proc x30 + + ud2 + + ret + +endproc + +proc x31 + + ud2 + +endproc + +proc x32 + + ud2 + +endproc + +proc x33 + + ud2 + +endproc + +proc x34 + + ud2 + +endproc + +proc x35 + + ud2 + +endproc + +proc x36 + + ud2 + +endproc + +proc x37 + + ud2 + +endproc + +proc x38 + + ud2 + +endproc + +proc x39 + + ud2 + +endproc + +proc x3a + + ud2 + +endproc + +proc x3b + + ud2 + +endproc + +proc x3c + + ud2 + +endproc + +proc x3d + + ud2 + +endproc + +proc x3e + + ud2 + +endproc + +proc x3f + + ud2 + +endproc