Merge pull request #203 from xexyl/1992-buzzard.1
Format and typo fixes in 1992/buzzard.1/README.md
This commit is contained in:
1 file changed
+140
-136
+140
-136
@@ -19,159 +19,164 @@ Most Obfuscated Algorithm:
|
|||||||
|
|
||||||
## Judges' comments
|
## Judges' comments
|
||||||
|
|
||||||
To see good C Preprocessor buzzard.1, try:
|
To see good C Preprocessor buzzard.1, try:
|
||||||
|
|
||||||
make buzzard.1.cppcb
|
make babble.cppcb
|
||||||
cat buzzard.1.cppcb
|
cat babble.cppcb
|
||||||
|
|
||||||
Notice how many statements it takes to do anything useful?
|
Notice how many statements it takes to do anything useful?
|
||||||
|
|
||||||
## Author's comments
|
## Author's comments;
|
||||||
|
|
||||||
What you shouldn't think about buzzard.1:
|
What you shouldn't think about buzzard.1:
|
||||||
|
|
||||||
buzzard.1 doesn't just do a mass of #defines as its obfuscation.
|
`buzzard.1` doesn't just do a mass of #defines as its obfuscation.
|
||||||
|
|
||||||
|
|
||||||
What you should be glad about:
|
### What you should be glad about:
|
||||||
|
|
||||||
I didn't just submit the post-C-preprocessed output as an entry.
|
I didn't just submit the post-C-preprocessed output as an entry.
|
||||||
|
|
||||||
|
#### Cute trick warning:
|
||||||
|
|
||||||
|
`buzzard.1` contains only a numeric printf, yet sometimes it prints out
|
||||||
|
short strings.
|
||||||
|
|
||||||
|
|
||||||
Cute trick warning:
|
### What it does:
|
||||||
|
|
||||||
buzzard.1 contains only a numeric printf, yet sometimes it prints out
|
`buzzard.1` is a simple mathematical program. It expects two numeric
|
||||||
short strings.
|
arguments.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% ./buzzard.1 0 <num>
|
||||||
|
```
|
||||||
|
|
||||||
|
will print out the factorial of `<num>`.
|
||||||
|
|
||||||
|
```
|
||||||
|
% ./buzzard.1 <num1> <num2>
|
||||||
|
```
|
||||||
|
|
||||||
|
will print out the largest common factor of `num1` and `num2`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% ./buzzard.1 1 <num2>
|
||||||
|
```
|
||||||
|
|
||||||
|
will print out a factor of `num2` if it's composite, or else
|
||||||
|
it will print a string informing you that `num2` is prime.
|
||||||
|
|
||||||
|
If the first argument is less than 0, it prints out an
|
||||||
|
error message.
|
||||||
|
|
||||||
|
|
||||||
What it does:
|
### What it is:
|
||||||
|
|
||||||
buzzard.1 is a simple mathematical program. It expects two numeric
|
`buzzard.1` is a translator from a pseudo-assembly language into a subset of C
|
||||||
arguments.
|
suitable for execution on Charles Babbage's Analytical Engine. Or rather, the
|
||||||
|
`#define`s in `buzzard.1` are that translator. The rest of buzzard.1 is a
|
||||||
% buzzard.1 0 <num>
|
buzzard.1 program. If you run the whole mess through CPP and a beautifier, you
|
||||||
will print out the factorial of <num>.
|
will see that all you have is a loop that runs until a variable is not 0, and a
|
||||||
|
sequence of assignment statements. The assignment statements are all of the
|
||||||
% buzzard.1 <num1> <num2>
|
form `a op= b` or `a = b`. The Engine actually allowed 'a = b op c'. Only `+`,
|
||||||
will print out the largest common factor of num1 and num2.
|
`-`, `/`, and `*` are used--no boolean or bitwise operators. The infinite loop
|
||||||
|
could have been simulated on the AE by connecting the already-processed card
|
||||||
% buzzard.1 1 <num2>
|
stack to the input card stack.
|
||||||
will print out a factor of num2 if it's composite, or else
|
|
||||||
it will print a string informing you that num2 is prime.
|
|
||||||
|
|
||||||
If the first argument is less than 0, it prints out an
|
|
||||||
error message.
|
|
||||||
|
|
||||||
|
|
||||||
What it is:
|
### How to try to understand it:
|
||||||
|
|
||||||
buzzard.1 is a translator from a pseudo-assembly language into a
|
*Don't* expand the `#define`s! Rather, decipher what they do.
|
||||||
subset of C suitable for execution on Charles Babbage's
|
Some of them are mere obfuscation fodder, put in to encourage
|
||||||
Analytical Engine. Or rather, the #defines in buzzard.1 are that
|
you to run it through CPP.
|
||||||
translator. The rest of buzzard.1 is a buzzard.1 program. If you run the
|
|
||||||
whole mess through CPP and a beautifier, you will see that
|
|
||||||
all you have is a loop that runs until a variable is not 0, and
|
|
||||||
a sequence of assignment statements. The assignment statements
|
|
||||||
are all of the form 'a op= b' or 'a = b'. The Engine actually
|
|
||||||
allowed 'a = b op c'. Only +, -, /, and * are used--no boolean
|
|
||||||
or bitwise operators. The infinite loop could have been
|
|
||||||
simulated on the AE by connecting the already-processed
|
|
||||||
card stack to the input card stack.
|
|
||||||
|
|
||||||
|
|
||||||
How to try to understand it:
|
### What it doesn't quite do right as an emulator:
|
||||||
|
|
||||||
*Don't* expand the #defines! Rather, decipher what they do.
|
Because of the definitional constraints, calls to `atoi(argv[#])` also appear
|
||||||
Some of them are mere obfuscational fodder, put in to encourage
|
inside the loop. These could be put outside-- the initial values of the
|
||||||
you to run it through CPP.
|
"registers" on the AE--but the macro conventions being used didn't lend
|
||||||
|
themselves to it.
|
||||||
|
|
||||||
|
|
||||||
What it doesn't quite do right as an emulator:
|
### Theoretical observations:
|
||||||
|
|
||||||
Because of the definitional constraints, calls to atoi(argv[#])
|
The simulated comparison operations only work with a certain
|
||||||
also appear inside the loop. These could be put outside--
|
range of numbers; numbers that are too large will cause the
|
||||||
the initial values of the "registers" on the AE--but the
|
output code to fail to simulate the input code. This means
|
||||||
macro conventions being used didn't lend themselves to it.
|
that this implementation of buzzard.1 would not be Turing-complete,
|
||||||
|
even if the AE could process indefinitely-sized numbers.
|
||||||
|
However, this is actually a constraint that no actual computer
|
||||||
|
can meet, either, so we can conclude that if an AE, with
|
||||||
|
card bins connected as suggested above, were hooked up to
|
||||||
|
a memory unit of sufficient size, it would be as Turing
|
||||||
|
complete as any existing machine (or, for those who
|
||||||
|
interpret that excessively critically, i.e. "not at all" for
|
||||||
|
both, we can simply say that the AE could simulate any
|
||||||
|
existing machine--although not quickly). This would be a
|
||||||
|
good place to site some references on the AE, but I don't
|
||||||
|
have any.
|
||||||
|
|
||||||
|
|
||||||
Theoretical observations:
|
### Definition of the input language to buzzard.1 (OR What Exactly Do All The Macros Do?):
|
||||||
|
|
||||||
The simulated comparison operations only work with a certain
|
[Don't read this unless you're stumped!]
|
||||||
range of numbers; numbers that are too large will cause the
|
|
||||||
output code to fail to simulate the input code. This means
|
|
||||||
that this implementation of buzzard.1 would not be Turing-complete,
|
|
||||||
even if the AE could process indefinitely-sized numbers.
|
|
||||||
However, this is actually a constraint that no actual computer
|
|
||||||
can meet, either, so we can conclude that if an AE, with
|
|
||||||
card bins connected as suggested above, were hooked up to
|
|
||||||
a memory unit of sufficient size, it would be as Turing
|
|
||||||
complete as any existing machine (or, for those who
|
|
||||||
interpret that excessively critically, i.e. "not at all" for
|
|
||||||
both, we can simply say that the AE could simulate any
|
|
||||||
existing machine--although not quickly). This would be a
|
|
||||||
good place to site some references on the AE, but I don't
|
|
||||||
have any.
|
|
||||||
|
|
||||||
|
|
||||||
Definition of the input language to buzzard.1:
|
|
||||||
(OR What Exactly Do All The Macros Do?)
|
|
||||||
|
|
||||||
[Don't read this unless you're stumped!]
|
|
||||||
|
|
||||||
|
|
||||||
MACRO FUNCTION MNEMONIC
|
MACRO FUNCTION MNEMONIC
|
||||||
V begin variable declarations Variable
|
|
||||||
C begin program Code
|
|
||||||
|
|
||||||
Q(b,a) let b = a eQuals
|
V begin variable declarations Variable
|
||||||
A(c,a,b) let c = a + b Add
|
C begin program Code
|
||||||
S(c,a,b) let c = a - b Subtract
|
|
||||||
D(c,a,b) let c = a / b, where b is a *constant* Divide
|
|
||||||
U(c,a,b) let c = a / b, where b is anything Unknown
|
|
||||||
M(c,a,b) let c = a * b Multiply
|
|
||||||
|
|
||||||
O(c,a,b) let c = a boolean-or b (a,b are 0 or 1) Or
|
Q(b,a) let b = a eQuals
|
||||||
B(b, a) let b = boolean value of a Boolean
|
A(c,a,b) let c = a + b Add
|
||||||
P(b, a) let b = 1 if a>0, else 0. Positive
|
S(c,a,b) let c = a - b Subtract
|
||||||
|
D(c,a,b) let c = a / b, where b is a *constant* Divide
|
||||||
|
U(c,a,b) let c = a / b, where b is anything Unknown
|
||||||
|
M(c,a,b) let c = a * b Multiply
|
||||||
|
|
||||||
l emit next sequential label Label
|
O(c,a,b) let c = a boolean-or b (a,b are 0 or 1) Or
|
||||||
J(x) goto label #x Jump
|
B(b, a) let b = boolean value of a Boolean
|
||||||
|
P(b, a) let b = 1 if a>0, else 0. Positive
|
||||||
|
|
||||||
Z(a,d) if a is 0 goto d (a is 0 or 1) Zero
|
l emit next sequential label Label
|
||||||
E(a,d) if a is 1 goto d (a is 0 or 1) Else
|
J(x) goto label #x Jump
|
||||||
|
|
||||||
H halt Haly
|
Z(a,d) if a is 0 goto d (a is 0 or 1) Zero
|
||||||
|
E(a,d) if a is 1 goto d (a is 0 or 1) Else
|
||||||
|
|
||||||
K(x) let x = number of cmd line arguments Kount
|
H halt Haly
|
||||||
G(x,y) let x = the value if the yth argument Get argument
|
|
||||||
|
|
||||||
T end of code; begin output section Terminate
|
K(x) let x = number of cmd line arguments Kount
|
||||||
X(y) print out hexadecimal value of y heXadecimal
|
G(x,y) let x = the value if the yth argument Get argument
|
||||||
T end of output section and program Terminate
|
|
||||||
|
|
||||||
You can figure out the other macros yourself. In the sample
|
T end of code; begin output section Terminate
|
||||||
program, I've actually implemented subroutines by saving
|
X(y) print out hexadecimal value of y heXadecimal
|
||||||
a return address in a variable and then jumping to a routine--
|
T end of output section and program Terminate
|
||||||
specifically, a routine that converts a number into bcd, so
|
|
||||||
it can be output by the hexadecimal output statement.
|
You can figure out the other macros yourself. In the sample program, I've
|
||||||
|
actually implemented subroutines by saving a return address in a variable and
|
||||||
|
then jumping to a routine-- specifically, a routine that converts a number into
|
||||||
|
bcd, so it can be output by the hexadecimal output statement.
|
||||||
|
|
||||||
|
|
||||||
How it works, i.e., how to get useful programs from a op= b
|
### How it works, i.e., how to get useful programs from a op= b
|
||||||
|
|
||||||
The essential statement to be able to do to do interesting
|
The essential statement to be able to do to do interesting
|
||||||
programs is the conditional (given that we have loops).
|
programs is the conditional (given that we have loops).
|
||||||
Since all you can do in the given operation output set is
|
Since all you can do in the given operation output set is
|
||||||
assignment, we implement "conditional assignment":
|
assignment, we implement "conditional assignment":
|
||||||
|
|
||||||
if (x) y = z;
|
if (x) y = z;
|
||||||
|
|
||||||
To implement this, we constrain x to be either 0 or 1, and
|
To implement this, we constrain x to be either 0 or 1, and
|
||||||
simply compute:
|
simply compute:
|
||||||
|
|
||||||
y = (z * x) + (y * (1-x));
|
y = (z * x) + (y * (1-x));
|
||||||
|
|
||||||
This is more obscured by factoring out common terms and restricting
|
This is more obscured by factoring out common terms and restricting
|
||||||
ourselves to two operand operations:
|
ourselves to two operand operations:
|
||||||
|
|
||||||
temp = z; (temp == z)
|
temp = z; (temp == z)
|
||||||
temp -= y; (temp == z-y)
|
temp -= y; (temp == z-y)
|
||||||
@@ -180,41 +185,40 @@ Most Obfuscated Algorithm:
|
|||||||
( == z*x - y*x + y)
|
( == z*x - y*x + y)
|
||||||
( == z*x + y*(1-x))
|
( == z*x + y*(1-x))
|
||||||
|
|
||||||
Next we imagine we have a pc. "I'm supposed to execute statement
|
Next we imagine we have a pc. "I'm supposed to execute statement
|
||||||
pc next", says our emulator. But suppose we're not currently
|
pc next", says our emulator. But suppose we're not currently
|
||||||
coming up on statement pc. To handle this, we simply make every
|
coming up on statement pc. To handle this, we simply make every
|
||||||
operation conditional on the pc having the correct value:
|
operation conditional on the pc having the correct value:
|
||||||
|
|
||||||
if (pc == some_constant) y = z;
|
if (pc == some_constant) y = z;
|
||||||
|
|
||||||
To combine this with other operations, we simply multiply by our
|
To combine this with other operations, we simply multiply by our
|
||||||
(pc == some_constant) flag right after the above 'temp *= x'.
|
(`pc == some_constant`) flag right after the above 'temp `*= x'`.
|
||||||
|
|
||||||
To evaluate pc == some_constant requires one more trick. We assume
|
To evaluate `pc == some_constant` requires one more trick. We assume that
|
||||||
that numbers can only be in some limited range. Then we use successive
|
numbers can only be in some limited range. Then we use successive additions and
|
||||||
additions and divisions to reduce that number down to -1, 0, or 1.
|
divisions to reduce that number down to `-1`, `0`, or `1`. You could, for
|
||||||
You could, for instance, do this by using mod by 2 and div by 2 to
|
instance, do this by using mod by `2` and div by `2` to count bits, stopping
|
||||||
count bits, stopping after, say, 32 iterations. The number you get
|
after, say, `32` iterations. The number you get is between `0..32`, so another
|
||||||
is between 0..32, so another 6 iterations on it reduces it to 0..6.
|
`6` iterations on it reduces it to 0..6. Three iterations on this produces
|
||||||
Three iterations on this produces 0..2 (3 would be '7'), and then
|
`0..2` (`3` would be `7`), and then two iterations on this produces 0 or 1.
|
||||||
two iterations on this produces 0 or 1. Instead I use a shorter
|
Instead I use a shorter two-divide approach that assumes I'm allowed to use
|
||||||
two-divide approach that assumes I'm allowed to use numbers slightly
|
numbers slightly larger than the numbers I'm operating on.
|
||||||
larger than the numbers I'm operating on.
|
|
||||||
|
|
||||||
A similar approach is used to detect positive numbers.
|
A similar approach is used to detect positive numbers.
|
||||||
|
|
||||||
Note that the obvious code to compute y/x will not work correctly,
|
Note that the obvious code to compute `y/x` will not work correctly,
|
||||||
because every value is computed, even if the pc is not set correctly;
|
because every value is computed, even if the pc is not set correctly;
|
||||||
if x is ever 0 when the real C code reaches this computation the
|
if `x` is ever `0` when the real C code reaches this computation the
|
||||||
program will die. (This was discovered the hard way.) The solution
|
program will die. (This was discovered the hard way.) The solution
|
||||||
is to stick x in a temporary, and if the current code is not about
|
is to stick `x` in a temporary, and if the current code is not about
|
||||||
to be executed, set the temporary to 1.
|
to be executed, set the temporary to `1`.
|
||||||
|
|
||||||
Thanks to Bill Pugh for bringing the loopability of the AE to
|
|
||||||
my attention.
|
|
||||||
|
|
||||||
buzzard.1 was originally named 'cb', for obvious reasons, and is
|
Thanks to Bill Pugh for bringing the loop-ability of the AE to
|
||||||
dedicated to the memory of Charles Barrett.
|
my attention.
|
||||||
|
|
||||||
|
`buzzard.1` was originally named 'cb', for obvious reasons, and is
|
||||||
|
dedicated to the memory of Charles Barrett.
|
||||||
|
|
||||||
## Copyright and CC BY-SA 4.0 License:
|
## Copyright and CC BY-SA 4.0 License:
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user