[Note: I/O has now been added]

My son is completely obsessed with Minecraft. It’s an amazing game and the way he plays it, it really nurtures extreme creativity. I honestly haven’t been able to get into it myself but I was pretty intrigued by the ComputerCraft mod which allows you to script the game in Lua. I thought it might be a good way to introduce my son to programming.

Even better, just today Notch announced his latest game and it looks like it will revolve around programming a 1980’s style 16-bit computer! Based on the spec at the site, I just couldn’t resist implementing the DCPU-16 in F# :) I can’t wait for the game to come out!

let memory = Array.create 0x10000 0us let registers = Array.create 11 0us let rec cycle () = let mem i = (fun () -> memory.[i] |> int), (fun v -> memory.[i] <- uint16 v) let reg i = (fun () -> registers.[i] |> int), (fun v -> registers.[i] <- uint16 v) let lit v = (fun () -> v), (fun _ -> ()) let pc, sp, o = reg 8, reg 9, reg 10 let get v = (fst v) () let set v i = (snd v) i let next () = let p = get pc in let n = mem p |> get in set pc (p + 1); n let decode () = let i = next () in i, (i &&& 0x3f0) >>> 4 |> int, (i &&& 0xfc00) >>> 10 |> int let skip () = let skip' i = if (i > 0xf && i < 0x18) || i = 0x1e || i = 0x1f then get pc + 1 |> set pc else () let _, a, b = decode () skip' a; skip' b let value = function | x when x <= 0x07 -> reg x // reg | x when x <= 0x0f -> (x &&& 0b111 |> reg |> fst) () |> mem // [reg] | x when x <= 0x17 -> (x &&& 0b111 |> reg |> fst) () + next () |> mem // [next + reg] | 0x18 -> let s = get sp in let x = mem s in set sp (s + 1); x // POP / [SP++] | 0x19 -> get sp |> mem // PEEK / [SP] | 0x1a -> get sp - 1 |> set sp; get sp |> mem // PUSH / [--SP] | 0x1b -> sp // SP | 0x1c -> pc // PC | 0x1d -> o // O | 0x1e -> next () |> mem // [next] | 0x1f -> next () |> lit // next (literal) | x -> x - 0x20 |> lit // literal 0x00-0x1f let instruction, a', b' = decode () let a, b = value a', value b' let fxy f = f (get a) (get b) let fop op = fxy (fun x y -> op x y |> set a) let fif op = fxy (fun x y -> if op x y then skip ()) match instruction &&& 0xf |> int with | 0x0 -> if a' = 0x01 then get pc |> set (value 0x1a (* PUSH *)); get b |> set pc // JSR | 0x1 -> get b |> set a // SET | 0x2 -> fxy (fun x y -> x + y |> set a; set o (if x + y > 0xffff then 1 else 0)) // ADD | 0x3 -> fxy (fun x y -> x - y |> set a; set o (if x - y < 0 then 0xffff else 0)) // SUB | 0x4 -> fxy (fun x y -> x * y |> set a; ((x * y) >>> 16) |> set o) // MUL | 0x5 -> fxy (fun x y -> if y = 0 then set a 0; set o 0 else x / y |> set a; (x <<< 16) / y |> set o) // DIV | 0x6 -> fxy (fun x y -> set a (if y = 0 then 0 else x % y)) // MOD | 0x7 -> fxy (fun x y -> let z = x <<< y in set a z; z >>> 16 |> set o) // SHL | 0x8 -> fxy (fun x y -> x >>> y |> set a; (x <<< 16) >>> y |> set o) // SHR | 0x9 -> fop (&&&) // AND | 0xa -> fop (|||) // BOR | 0xb -> fop (^^^) // XOR | 0xc -> fif (<>) // IFE | 0xd -> fif (=) // IFN | 0xe -> fif (<=) // IFG | 0xf -> fif (fun x y -> x &&& y = 0) // IFB cycle ()