How tiny is this computer? So tiny that it has only two instructions:
inc i = Increment the value in register i by one and increment the instruction pointer (ip).
jzd i j = If the value in register i is zero, set the instruction pointer to j. If the value is not zero, decrement the value in register i by one and increment the instruction pointer.
The basic operation is simple: starting at ip=0, retrieve and execute an instruction. Continue until the ip is either less than zero or greater than the index of the last instruction. When complete, return as a result the value of a specified register.
Below is an implementation in F#. Note that the registers are implemented as indices into an array of integers. A single-step function, “step” is provided for debugging. Note that “run” has a basic safety check against infinite loops. The code also shows some basic operations and a test. (As always, all the code here is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)
/// Increment register i and
/// advance the instruction pointer.
let inc i (d:int []) ip =
d.[i]<-d.[i]+1
ip+1
/// If register i is zero, jump to ipj,
/// else, decrement register i and
/// advance the instruction pointer.
let jzd i j (d:int []) ip =
match d.[i] with
| 0 -> j
| _ ->
d.[i]<-d.[i]-1
ip+1
/// Single-step and instruction.
/// Returns the new ip, or
/// -1 on under/overrun or halt.
let step (d:int [])
(p:(int []->int->int)[])
ip =
match (ip<0) || (ip>=p.Length) with
| true -> -1
| _ -> p.[ip] d ip
/// Run a program.
/// Halts on an ip of -1 or
/// when safety limit is reached.
/// Returns the value of register rtn.
let run (d:int [])
(p:(int []->int->int)[])
rtn =
let rec run0 count ip =
match count>=System.Int32.MaxValue with
| true -> failwith "Loop safty exceeded."
| _ ->
match step d p ip with
| -1 -> d.[rtn]
| ip0 -> run0 (count+1) ip0
run0 0 0
/// Five register memory.
let d5 = [|0;100;0;0;|]
/// Zero out register 1.
let pZero1 =
[|
(*00*) jzd 1 -1
(*01*) jzd 0 0
|]
/// Set the value in register 1 to 5.
let pSet1To5 =
[|
// Zero out 1.
(*00*) jzd 1 2
(*01*) jzd 0 0
// Increment 5 times.
(*02*) inc 1
(*03*) inc 1
(*04*) inc 1
(*05*) inc 1
(*06*) inc 1
|]
/// Copy the value in register 1 to
/// register 2.
let pCopy1To2 =
[|
(*00*) jzd 2 2
(*01*) jzd 0 0
(*02*) jzd 3 4
(*03*) jzd 0 2
(*04*) jzd 1 8
(*05*) inc 2
(*06*) inc 3
(*07*) jzd 0 4
(*08*) jzd 3 -1
(*09*) inc 1
(*10*) jzd 0 8
|]
// Tests.
let x0 = run d5 pZero1 1
let x1 = run d5 pSet1To5 1
let x2 = run d5 pCopy1To2 2
Now here is the integer square root program. (In order to make the program more readable, I define some constants and macros which do not alter the basic operation of the machine.)
/// Six register memory.
let d6 = [|0;0;0;0;0;0;|]
/// Give the registers labels.
let zero = 0
let n = 1
let odd = 2
let sqrt = 3
let tmp0 = 4
let tmp1 = 5
// Jumping to -1 will halt.
let halt = -1
/// Define jmp macro.
// Note: this is just a macro
// dependent on there being a
// register with a constant value
// of zero. It does not increase
// the size of the instruction set.
let jmp = jzd zero
/// Compute the integer square root
/// of the value in register n,
/// and return the result in register sqrt.
let pSqrt =
[|
// Set odd to 1.
(*00*) jzd odd 2
(*01*) jmp 0
(*02*) inc odd
// Set tmp0 to zero.
(*03*) jzd tmp0 5
(*04*) jmp 3
// Set tmp1 to zero.
(*05*) jzd tmp1 7
(*06*) jmp 5
// Copy odd to tmp0 and tmp1.
(*07*) jzd odd 11
(*08*) inc tmp0
(*09*) inc tmp1
(*10*) jmp 7
// Subtract tmp0 from n.
(*11*) jzd tmp0 14
(*12*) jzd n halt
(*13*) jzd zero 11
// Copy tmp1 to odd
(*14*) jzd tmp1 17
(*15*) inc odd
(*16*) jmp 14
// Next odd.
(*17*) inc odd
(*18*) inc odd
// Increment Sqrt.
(*19*) inc sqrt
// Loop.
(*20*) jmp 7
|]
// This is cheating a bit,
// but it beats looking at
// 80K copies of "inc n", lol.
// 80K is convenient because its
// square root is Sqrt(2)*100.
let test = 80000
d6.[n]<-test
// Find the square root.
// Prints are formatted weird to fit in blog.
printf "The largest integer not exceeding "
printf "the square root of %i is " test
printfn "%i." (run d6 pSqrt sqrt)
And below is an extension of the above, being a multiplication program which computes the square of the result from above. (A good exercise would be to preserve some remainder from the previous program and add it back in here.)
// To check the answer, here is a
// multiplication routine.
// Alias the older labels.
let mul0 = n
let mul1 = sqrt
let prod = odd
/// Copy the value in mul1 (sqrt) to mul0 (n).
let pCopyMul1ToMul0 =
[|
// Zero out mul0.
(*00*) jzd mul0 2
(*01*) jmp 0
// Zero out tmp0.
(*02*) jzd tmp0 4;
(*03*) jmp 2;
// Move mul1 to mul0,tmp0
(*04*) jzd mul1 8;
(*05*) inc mul0
(*06*) inc tmp0
(*07*) jmp 4;
// Move tmp0 back to mul1.
(*08*) jzd tmp0 halt
(*09*) inc mul1
(*10*) jmp 8
|]
/// Mutliply mul0 by mul1.
let pMul0TimesMul1 =
[|
// Zero out tmp0.
(*00*) jzd tmp0 2;
(*01*) jmp 0;
// Zero out prod.
(*02*) jzd prod 4;
(*03*) jmp 2;
// Main loop on mul1.
(*04*) jzd mul1 halt
// Move mul0 to tmp0 and add to prod.
(*05*) jzd mul0 9
(*06*) inc tmp0
(*07*) inc prod
(*08*) jmp 5
// Move tmp0 to mul0.
// Loop back when done.
(*09*) jzd tmp0 4
(*10*) inc mul0
(*12*) jmp 9
|]
// Mutlipy the square root by itself.
// Prints are formatted weird to fit in blog.
run d6 pCopyMul1ToMul0 zero |> ignore
printf "The largest perfect square not exceeding "
printf "%i is %i." test (run d6 pMul0TimesMul1 prod)
So simple that one could probably construct a programmable machine in wood and metal for programs such as the above! It is easy to imagine how it might work: simple counters that can always be incremented, but which will stop when decremented to zero. Something to transfer these stops at zero to program jumps. A program perhaps stored on a wheel that rotates from ip to ip. Etc.
It almost makes me want to head down to the workshop after dinner, lol.
-Neil
p.s. The above virtual machine programs may not be minimal. They were the obvious approaches.
p.p.s. Isn't it neat how F# allows the creation of a domain-specific language using arrays that looks almost like source code for a real assembly language program?
No comments:
Post a Comment