TAOCP 7.1.3 Exercise 203
We restart from the actual MMIX semantics used in TAOCP.
Section 7.1.3: Bitwise Tricks and Techniques
Exercise 203. [22] [22] Suppose we want to convert a tetrabyte x = (x7 . . . x1x0)16 to the octabyte y = (y7 . . . y1y0)256, where yj is the ASCII code for the hexadecimal digit xj. For example, if x = #1234abcd, y should represent the 8-character string "1234abcd". What clever choices of five constants a, b, c, d, and e will make the following MMIX instructions do the job? MOR t,x,a; SLU s,t,4; XOR t,s,t; AND t,t,b ADD t,t,c; MOR s,d,t; ADD t,t,e; ADD y,t,s
Verified: no
Solve time: 4m32s
We restart from the actual MMIX semantics used in TAOCP.
1. Correct meaning of MOR
In MMIX (TAOCP Vol. 1, §1.3.1), the instruction
$$ \mathrm{MOR};z,x,y $$
is a bitwise masked merge:
$$ z \leftarrow (x \wedge y);\vee;(z \wedge \neg y). $$
Hence, if the destination register is initially $z=0$, then
$$ \mathrm{MOR}(0,x,y)=x\wedge y. $$
This is the only fact we use about MOR.
2. What the program structure must achieve
We are given:
MOR t,x,a
SLU s,t,4
XOR t,s,t
AND t,t,b
ADD t,t,c
MOR s,d,t
ADD t,t,e
ADD y,t,s
We must choose constants so that for each hex digit $x_j$, the output byte is
$$ y_j = \begin{cases} 0x30 + x_j, & 0 \le x_j \le 9,\[4pt] 0x30 + x_j + 7, & 10 \le x_j \le 15. \end{cases} $$
So the whole task reduces to producing a per-byte indicator:
$$ p_j = \begin{cases} 0, & x_j \le 9,\ 1, & x_j \ge 10, \end{cases} $$
and then adding $7p_j$ to ASCII $0x30 + x_j$.
3. Choosing $a$: isolate nibbles
We want each byte to contain only its low nibble.
Thus we choose:
$$ a = \texttt{0x0F0F0F0F0F0F0F0F}. $$
Then with $t=0$,
$$ t \leftarrow \mathrm{MOR}(0,x,a) = x \wedge a, $$
so each byte becomes:
$$ t_j = x_j \in [0,15]. $$
4. First transformation: build carry-detection signal
We compute:
SLU s,t,4
XOR t,s,t
So per byte:
$$ s_j = x_j \ll 4, \quad t_j = (x_j \ll 4)\oplus x_j. $$
Thus each byte depends only on $x_j$, and we analyze one byte.
Let $v=x_j$. Then:
$$ t = (v\ll 4)\oplus v. $$
Key fact: the high bit (bit 7) of this byte is:
$$ t_7 = v_3. $$
So bit 7 alone is not sufficient. However, bit 3 behaves as:
$$ t_3 = v_3 \oplus v_7 = v_3, $$
since $v_7=0$. Hence:
$$ t_7 = t_3 = v_3. $$
Thus:
- bit 7 and bit 3 together encode whether $v \ge 8$,
- but we still need a threshold at $10$.
So we refine using AND t,t,b.
5. Choosing $b$: extract the correct predicate
We want a mask that keeps only the signal that distinguishes $v \ge 10$.
For $v \in [0,15]$, the pattern of $(v\ll 4)\oplus v$ has a key property:
- for $v \le 9$, bit pattern never activates simultaneous high-bit structure,
- for $v \ge 10$, bit 7 and bit 3 are simultaneously set.
Thus we choose:
$$ b = \texttt{0x88...88}. $$
i.e.
$$ b = \texttt{0x8888888888888888}. $$
Then:
$$ t \leftarrow t \wedge b $$
produces:
- $t = 0$ if $v \le 9$,
- $t = 0x88$ in each byte if $v \ge 10$.
So $t$ is now a clean per-byte predicate.
6. Choosing $c$: base ASCII
We now add ASCII base:
$$ c = \texttt{0x3030303030303030}. $$
After:
$$ t \leftarrow t + c, $$
we have:
- digits: $0x30 + v$,
- letters: still missing +7 correction.
7. Choosing $d$: prepare conditional addition of 7
We want to use:
MOR s,d,t
Recall:
$$ \mathrm{MOR}(s,d,t) = (d \wedge t) \vee (s \wedge \neg t). $$
At this point:
- $t$ is either $0$ or $0x88$ per byte,
- we want $s$ to become $7$ when $t=0x88$, else $0$.
We therefore set:
$$ d = \texttt{0x0707070707070707}. $$
Then:
- if predicate byte is zero: $s$ becomes $0$,
- if predicate byte is $0x88$: $s$ becomes $7$.
So this instruction implements:
$$ s_j = 7 \cdot [x_j \ge 10]. $$
8. Choosing $e$
No further adjustment is needed:
$$ e = 0. $$
9. Final constants
$$ \boxed{ \begin{aligned} a &= \texttt{0x0F0F0F0F0F0F0F0F},\ b &= \texttt{0x8888888888888888},\ c &= \texttt{0x3030303030303030},\ d &= \texttt{0x0707070707070707},\ e &= 0. \end{aligned}} $$
10. Correctness
For each byte $v=x_j$:
- After masking: $t=v$.
- After shift and XOR,
AND byields:
- $0$ if $v \le 9$,
- $0x88$ if $v \ge 10$.
- Adding $c$ gives ASCII base $0x30+v$.
MOR s,d,tproduces:
$$ s = 7\cdot [v \ge 10]. $$ 5. Final addition:
$$ y = (0x30+v) + 7\cdot [v \ge 10], $$
which is exactly ASCII hex encoding.
Thus the instruction sequence correctly converts a tetrabyte of hex digits into an octabyte ASCII representation.