Last active
December 21, 2021 20:12
-
-
Save binji/b97c8d29d7a7bfbfbaac8265d0a4d52a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(import "env" "memory" (memory 1)) | |
(import "env" "text" (func $text (param i32 i32 i32))) | |
;; The current game mode. 0: init, 1: wait, 2: reset, 3:game, 4:winning | |
(global $mode (mut i32) (i32.const 0)) | |
;; The mode timer, in frames. | |
(global $mode-timer (mut i32) (i32.const 60)) | |
;; The maximum wall to render or collide against. Used to clear the maze. | |
(global $max-wall-addr (mut i32) (i32.const 0x2778)) | |
;; Position and direction vectors. Direction is updated from angle, which is | |
;; expressed in radians. | |
(global $Px (mut f32) (f32.const 21)) | |
(global $Py (mut f32) (f32.const 21)) | |
(global $angle (mut f32) (f32.const 0.7853981633974483)) | |
(global $ray-x (mut f32) (f32.const 0)) | |
(global $ray-y (mut f32) (f32.const 0)) | |
(global $timer-running (mut i32) (i32.const 0)) | |
(global $time (mut f32) (f32.const 0)) | |
(global $best-time (mut f32) (f32.const inf)) | |
;; Common constants. Cheap way to reduce the binary size. | |
(global $half-screen-height f32 (f32.const 80)) | |
(global $zero f32 (f32.const 0)) | |
(global $one-half f32 (f32.const 0.5)) | |
(global $one f32 (f32.const 1)) | |
;; The "time" of the ray-line collision along the wall in the range [0,1]. | |
(global $min-t2 (mut f32) (f32.const 0)) | |
;; The address of the wall hit by the most recent raycast. | |
(global $min-wall (mut i32) (i32.const 0)) | |
;; Color: u32 ; ABGR | |
;; Cell2: u8*2 ; left/up cell, right/down cell | |
;; Wall: s8*4,u8*4 ; (x0,y0),(dx, dy), scale/texture/palette/dummy | |
;; [0x0016, 0x0017) u8[4] DURL__ZX | |
;; [0x19a0, 0x19a4) f32 rotation speed | |
;; [0x19a4, 0x19a8) f32 speed | |
;; [0x19ac, 0x1a3c) u8[12*12] maze cells for Kruskal's algo | |
;; [0x1a3c, 0x1b2e) Cell2[11*11] walls for Kruskal's algo | |
;; [0x1b2e, 0x1c4c) Cell2[120] extra walls that are removed to make maze | |
;; [0x1c4c, 0x204c) u8[32*32] 8bpp brick texture | |
;; [0x204c, 0x244c) u8[32*32] 8bpp spot texture | |
;; [0x244c, 0x262c) f32[80] Table of 80(80-y) | |
;; [0x262c, 0x272c) u8[32*32] RLE compressed 2bpp textures | |
;; [0x272c, 0x2748) u8[28] color index (into Palette table @0x2b4c) | |
;; [0x2748, 0x2778) Wall[6] constant walls | |
;; [0x2778, 0x2b40) Wall[11*11] generated walls | |
;; [0x2b4c, 0x494c) Color[80][16] Palette table (80 levels of darkness) | |
;; [0x5000, 0x5500) f32[160*2] Floyd-Steinberg error table | |
(data (i32.const 4) | |
;; final 4-color palette | |
"\40\10\10\ff" | |
"\50\cf\50\ff" | |
"\5f\5f\c3\ff" | |
"\c8\cb\c0\ff" | |
;; draw color (for text) | |
"\01" | |
) | |
(data (i32.const 0x262c) | |
;; brick texture 2bpp RLE compressed | |
"\08\aa\04\00\ff\02\03\00\03\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06" | |
"\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52" | |
"\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5" | |
"\52\03\55\04\ff\ff\f2\03\ff\08\aa\ff\02\07\00\ff\52\06\55\fe\d5\52\06\55" | |
"\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06" | |
"\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52\06\55\fe\d5\52" | |
"\06\55\fe\d5\52\06\55\fe\d5\f2\07\ff" | |
;; floor and ceiling texture 2bpp RLE compressed | |
"\fe\00\56\05\55\fd\02\80\56\05\55\fe\0a\a0\06\55\fe\29\68\06\55\fe\a5\5a" | |
"\06\55\ff\95\3b\55\fe\95\5a\06\55\fe\a5\68\06\55\fe\29\a0\06\55\fd\0a\80" | |
"\56\05\55\fd\02\00\56\05\55\fd\0a\80\56\05\55\fe\29\a0\06\55\fe\a5\68\06" | |
"\55\fe\95\5a\3b\55\ff\5a\06\55\fe\95\68\06\55\fe\a5\a0\06\55\fd\29\80\56" | |
"\05\55\ff\0a" | |
;; 0x272c | |
;; left-right brick palette | |
"\00\04\08\0c" | |
;; top-bottom brick palette | |
"\10\14\18\1c" | |
;; ceiling palette | |
"\20\20\24\24" | |
;; floor palette | |
"\28\2c\2c\2c" | |
;; goal palette | |
"\30\34\38\3c" | |
;; left-right spot palette | |
"\04\00\08\0c" | |
;; top-bottom spot palette | |
"\14\10\18\1c" | |
;; 0x2748 | |
;; bottom wall | |
"\00\00\18\00" ;; (0,0),(24,0) | |
"\18\00\00\00" ;; scale:24, tex:0, pal:0 | |
;; right wall (minus goal) | |
"\18\00\00\16" ;; (24,0),(0,22) | |
"\16\00\04\00" ;; scale:22, tex:0, pal:1<<2 | |
;; right goal | |
"\18\16\00\02" ;; (24,22),(0,2) | |
"\02\00\10\00" ;; scale:2, tex:0, pal:4<<2 | |
;; top goal | |
"\18\18\fe\00" ;; (24,24),(-2,0) | |
"\02\00\10\00" ;; scale:2, tex:0, pal:4<<2 | |
;; top wall (minus goal) | |
"\16\18\ea\00" ;; (22,24),(-22,0) | |
"\16\00\00\00" ;; scale:22, tex:0, pal:0 | |
;; left wall | |
"\00\18\00\e8" ;; (0,24),(0,-24) | |
"\18\00\04\00" ;; scale:24, tex:0, pal:1<<2 | |
) | |
(data (i32.const 0x2b4c) | |
;; brightest versions of all 16 colors | |
"\f3\5f\5f\ff\9f\25\25\ff\51\0a\0a\ff\79\0e\0e\ff" | |
"\c2\4c\4c\ff\7f\1d\1d\ff\51\0a\0a\ff\60\0b\0b\ff" | |
"\cb\c8\b8\ff\9b\97\81\ff\81\95\af\ff\b5\b5\b5\ff" | |
"\10\ff\10\ff\10\ef\10\ff\10\df\10\ff\10\cf\10\ff") | |
;; Conversion to wasm by Ben Smith. | |
;; | |
;; Conversion to float by Ian Lance Taylor, Cygnus Support, [email protected]. | |
;; Optimized by Bruce D. Evans. | |
;; | |
;; ==================================================== | |
;; Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. | |
;; | |
;; Developed at SunPro, a Sun Microsystems, Inc. business. | |
;; Permission to use, copy, modify, and distribute this | |
;; software is freely granted, provided that this notice | |
;; is preserved. | |
;; ==================================================== | |
;; | |
(func $sindf (param $x f64) (result f32) | |
(local $r f64) | |
(local $s f64) | |
(local $w f64) | |
(local $z f64) | |
(local.set $z (f64.mul (local.get $x) (local.get $x))) | |
(local.set $w (f64.mul (local.get $z) (local.get $z))) | |
(local.set $r (f64.mul (f64.const -0x1a00f9e2cae774.0p-65) | |
(f64.mul (local.get $z) | |
(f64.const 0x16cd878c3b46a7.0p-71)))) | |
(local.set $s (f64.mul (local.get $z) (local.get $x))) | |
(f32.demote_f64 | |
(f64.add | |
(f64.add | |
(local.get $x) | |
(f64.mul | |
(local.get $s) | |
(f64.add | |
(f64.const -0x15555554cbac77.0p-55) | |
(f64.mul | |
(local.get $z) | |
(f64.const 0x111110896efbb2.0p-59))))) | |
(f64.mul | |
(f64.mul | |
(local.get $s) | |
(local.get $w)) | |
(local.get $r)))) | |
) | |
(func $cosdf (param $x f64) (result f32) | |
(local $r f64) | |
(local $w f64) | |
(local $z f64) | |
(local.set $z (f64.mul (local.get $x) (local.get $x))) | |
(local.set $w (f64.mul (local.get $z) (local.get $z))) | |
(local.set $r (f64.mul (f64.const -0x16c087e80f1e27.0p-62) | |
(f64.mul (local.get $z) | |
(f64.const 0x199342e0ee5069.0p-68)))) | |
(f32.demote_f64 | |
(f64.add | |
(f64.add | |
(f64.add | |
(f64.const 1.0) | |
(f64.mul | |
(local.get $z) | |
(f64.const -0x1ffffffd0c5e81.0p-54))) | |
(f64.mul | |
(local.get $w) | |
(f64.const 0x155553e1053a42.0p-57))) | |
(f64.mul | |
(f64.mul | |
(local.get $w) | |
(local.get $z)) | |
(local.get $r))))) | |
(func $sin-helper (param $x f64) (param $fn f64) (result f64) | |
(f64.sub | |
(f64.sub | |
(local.get $x) | |
(f64.mul | |
(local.get $fn) | |
(f64.const 1.57079631090164184570e+00))) | |
(f64.mul | |
(local.get $fn) | |
(f64.const 1.58932547735281966916e-08)))) | |
(func $sin (param $x f32) (result f32) | |
(local $ix i32) | |
(local $sign i32) | |
(local $n i32) | |
(local $x64 f64) | |
(local $fn f64) | |
(local $y f64) | |
(local.set $x64 (f64.promote_f32 (local.get $x))) | |
(local.set $ix (i32.reinterpret_f32 (local.get $x))) | |
(local.set $sign (i32.shr_u (local.get $ix) (i32.const 31))) | |
(local.set $ix (i32.and (local.get $ix) (i32.const 0x7fffffff))) | |
(if (i32.le_u (local.get $ix) (i32.const 0x3f490fda)) ;; |x| ~<= pi/4 | |
(then | |
(return | |
(if (result f32) | |
(i32.lt_u (local.get $ix) (i32.const 0x39800000)) ;; |x| < 2**-12 | |
(then | |
(local.get $x)) | |
(else | |
(call $sindf (local.get $x64))))))) | |
(local.set $fn | |
(f64.sub | |
(f64.add | |
(f64.mul | |
(local.get $x64) | |
(f64.const 6.36619772367581382433e-01)) | |
(f64.const 6755399441055744.0)) | |
(f64.const 6755399441055744.0))) | |
(local.set $n (i32.trunc_f64_s (local.get $fn))) | |
(if (f64.lt (local.get $y) (f64.const -0x1.921fb6p-1)) | |
(then | |
(local.set $n (i32.sub (local.get $n) (i32.const 1))) | |
(local.set $fn (f64.sub (local.get $fn) (f64.const 1)))) | |
(else | |
(if (f64.gt (local.get $y) (f64.const 0x1.921fb6p-1)) | |
(then | |
(local.set $n (i32.add (local.get $n) (i32.const 1))) | |
(local.set $fn (f64.add (local.get $fn) (f64.const 1))))))) | |
(local.set $y (call $sin-helper (local.get $x64) (local.get $fn))) | |
block block block block | |
(br_table 0 1 2 3 (i32.and (local.get $n) (i32.const 3))) | |
end ;; 0 | |
(return (call $sindf (local.get $y))) | |
end ;; 1 | |
(return (call $cosdf (local.get $y))) | |
end ;; 2 | |
(return (call $sindf (f64.neg (local.get $y)))) | |
end ;; 3 | |
(return (f32.neg (call $cosdf (local.get $y)))) | |
) | |
(; Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" | |
modified to return uniform float in range [0,1) ;) | |
(global $random-state (mut i32) (i32.const 1)) | |
(func $random (result f32) | |
(local $x i32) | |
(global.set $random-state | |
(local.tee $x | |
(i32.xor | |
(local.tee $x | |
(i32.xor | |
(local.tee $x | |
(i32.xor | |
(local.tee $x (global.get $random-state)) | |
(i32.shl | |
(local.get $x) | |
(i32.const 13)))) | |
(i32.shr_u | |
(local.get $x) | |
(i32.const 17)))) | |
(i32.shl | |
(local.get $x) | |
(i32.const 5))))) | |
(f32.mul | |
(f32.convert_i32_u (i32.shr_u (local.get $x) (i32.const 8))) | |
(f32.const 0x1p-24)) | |
) | |
;; Shoot a ray against all walls in the scene, and return the minimum distance | |
;; (or inf if no wall was hit). | |
;; This function also sets $min-t2 and $min-wall. | |
(func $ray-walls (result f32) | |
(local $wall i32) | |
(local $min-t1 f32) | |
(local $v1x f32) | |
(local $v1y f32) | |
(local $v2x f32) | |
(local $v2y f32) | |
(local $v2-dot-ray-perp f32) | |
(local $t1 f32) | |
(local $t2 f32) | |
(local.set $min-t1 (f32.const inf)) | |
(local.set $wall (i32.const 0x2748)) | |
(loop $wall-loop | |
;; Ray/line segment intersection. | |
;; see https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/ | |
(if | |
(i32.and (i32.and (i32.and | |
;; $t2 must be between [0, 1]. | |
(f32.ge | |
;; $t2 = intersection "time" between s and e. | |
;; = dot($v1, perp(ray)) / dot($v2, perp(ray)) | |
(local.tee $t2 | |
(f32.div | |
(f32.add | |
(f32.mul | |
;; $v1 = P - wall.P | |
(local.tee $v1x | |
(f32.sub | |
(global.get $Px) | |
(f32.convert_i32_s (i32.load8_s (local.get $wall))))) | |
(f32.neg (global.get $ray-y))) | |
(f32.mul | |
(local.tee $v1y | |
(f32.sub | |
(global.get $Py) | |
(f32.convert_i32_s (i32.load8_s offset=1 (local.get $wall))))) | |
(global.get $ray-x))) | |
;; $v2 = wall.dP | |
;; $v2-dot-ray-perp = dot($v2, perp(ray)) | |
(local.tee $v2-dot-ray-perp | |
(f32.add | |
(f32.mul | |
(local.tee $v2x | |
(f32.convert_i32_s (i32.load8_s offset=2 (local.get $wall)))) | |
(f32.neg (global.get $ray-y))) | |
(f32.mul | |
(local.tee $v2y | |
(f32.convert_i32_s (i32.load8_s offset=3 (local.get $wall)))) | |
(global.get $ray-x)))))) | |
(global.get $zero)) | |
(f32.le (local.get $t2) (global.get $one))) | |
;; $t1 is distance along ray, which must be >= 0. | |
(f32.ge | |
(local.tee $t1 | |
(f32.div | |
(f32.sub | |
(f32.mul (local.get $v2x) (local.get $v1y)) | |
(f32.mul (local.get $v1x) (local.get $v2y))) | |
(local.get $v2-dot-ray-perp))) | |
(global.get $zero))) | |
;; If the ray was a closer hit, update the min values. | |
(f32.lt (local.get $t1) (local.get $min-t1))) | |
(then | |
(local.set $min-t1 (local.get $t1)) | |
;; Scale t2 by the wall's scale value. | |
(global.set $min-t2 | |
(f32.mul | |
(local.get $t2) | |
(f32.convert_i32_u | |
(i32.load8_u offset=4 (local.get $wall))))) | |
(global.set $min-wall (local.get $wall)))) | |
(br_if $wall-loop | |
(i32.lt_s | |
(local.tee $wall (i32.add (local.get $wall) (i32.const 8))) | |
(global.get $max-wall-addr)))) | |
(local.get $min-t1)) | |
;; Takes an f32, doubles it, then take the fractional part of that and scales | |
;; it to [0, 32). Used to index into a 32x32 texture. | |
(func $scale-frac-i32 (param $x f32) (result f32) | |
(f32.mul | |
(f32.sub | |
(local.tee $x (f32.add (local.get $x) (local.get $x))) | |
(f32.floor (local.get $x))) | |
(f32.convert_i32_s (i32.const 32)))) | |
;; Returns a color from a 32x32 8bpp texture, using a two-level palette. | |
;; The texture contains a palette index [0, 4). | |
;; Each palette entry has a color index [0, 16). | |
;; The color index can be combined with a distance value [0, 80) to get the | |
;; actual 32-bit color. | |
(func $texture | |
(param $tex i32) (param $pal-addr i32) (param $dist i32) | |
(param $u f32) (param $v f32) | |
(result i32) | |
;; Read color from color-distance table. | |
(i32.load offset=0x2b4c | |
(i32.add | |
(i32.shl (local.get $dist) (i32.const 6)) | |
;; Read palette entry from palette. | |
(i32.load8_u offset=0x272c | |
(i32.add | |
(local.get $pal-addr) | |
;; Read from 32x32 texture. | |
(i32.load8_u offset=0x1c4c | |
(i32.add | |
(i32.shl (local.get $tex) (i32.const 10)) | |
(i32.add | |
;; wrap v coordinate to [0, 32), then multiply by 32. | |
(i32.shl | |
(i32.trunc_f32_s (call $scale-frac-i32 (local.get $v))) | |
(i32.const 5)) | |
;; wrap u coordinate to [0, 32). | |
(i32.trunc_f32_s (call $scale-frac-i32 (local.get $u))))))))))) | |
;; bayer matrix ordered dithering with arbitrary palette | |
;; see https://gist.github.com/yyny/3b54ff26ab4f8fd0aad468dd144191c2 | |
(func $0-255->0-1 (param $x i32) (result f32) | |
(f32.div | |
(f32.convert_i32_u (local.get $x)) | |
(f32.const 255))) | |
(func $0-1->0-255 (param $x f32) (result i32) | |
(i32.trunc_f32_u | |
(f32.mul | |
(local.get $x) | |
(f32.const 255)))) | |
(func $square (param $x f32) (result f32) | |
(f32.mul (local.get $x) (local.get $x))) | |
(func $luma (param $r f32) (param $g f32) (param $b f32) (result f32) | |
(f32.add | |
(f32.add | |
(f32.mul (local.get $r) (f32.const 0.299)) | |
(f32.mul (local.get $g) (f32.const 0.587))) | |
(f32.mul (local.get $b) (f32.const 0.114)))) | |
(func $color-distance (param $1 i32) (param $2 i32) (result f32) | |
(local $1r f32) | |
(local $1g f32) | |
(local $1b f32) | |
(local $2r f32) | |
(local $2g f32) | |
(local $2b f32) | |
(local $d f32) | |
(local $dluma f32) | |
(local.set $1b (call $0-255->0-1 (i32.and (local.get $1) (i32.const 255)))) | |
(local.set $1g (call $0-255->0-1 (i32.and (i32.shr_u (local.get $1) (i32.const 8)) (i32.const 255)))) | |
(local.set $1r (call $0-255->0-1 (i32.and (i32.shr_u (local.get $1) (i32.const 16)) (i32.const 255)))) | |
(local.set $2b (call $0-255->0-1 (i32.and (local.get $2) (i32.const 255)))) | |
(local.set $2g (call $0-255->0-1 (i32.and (i32.shr_u (local.get $2) (i32.const 8)) (i32.const 255)))) | |
(local.set $2r (call $0-255->0-1 (i32.and (i32.shr_u (local.get $2) (i32.const 16)) (i32.const 255)))) | |
(local.set $dluma | |
(f32.sub | |
(call $luma (local.get $2r) (local.get $2g) (local.get $2b)) | |
(call $luma (local.get $1r) (local.get $1g) (local.get $1b)))) | |
(f32.add | |
(f32.mul | |
(call $luma (call $square (f32.sub (local.get $2r) (local.get $1r))) | |
(call $square (f32.sub (local.get $2g) (local.get $1g))) | |
(call $square (f32.sub (local.get $2b) (local.get $1b)))) | |
(f32.const 0.75)) | |
(call $square (local.get $dluma))) | |
) | |
(func $closest-color (param $color i32) (result i32) | |
(local $i i32) | |
(local $best-i i32) | |
(local $distance f32) | |
(local $best-distance f32) | |
(local.set $best-distance (f32.const inf)) | |
(loop $loop | |
(local.set $distance | |
(call $color-distance | |
(local.get $color) | |
(i32.load offset=4 (local.get $i)))) | |
(if (f32.lt (local.get $distance) (local.get $best-distance)) | |
(then | |
(local.set $best-i (local.get $i)) | |
(local.set $best-distance (local.get $distance)))) | |
(br_if $loop | |
(i32.lt_s | |
(local.tee $i (i32.add (local.get $i) (i32.const 4))) | |
(i32.const 16)))) | |
(local.get $best-i) | |
) | |
(func $clamp01 (param $x f32) (result f32) | |
(f32.min | |
(f32.max (local.get $x) (f32.const 0)) | |
(f32.const 1))) | |
(func $pixel (param $x i32) (param $y i32) (param $color i32) | |
(local $palidx i32) | |
(local $addr i32) | |
(local $shift i32) | |
(local $error-addr i32) | |
(local $r f32) | |
(local $g f32) | |
(local $b f32) | |
(local $new-color i32) | |
(local $error-r f32) | |
(local $error-g f32) | |
(local $error-b f32) | |
(local.set $error-b | |
(f32.load offset=0x5000 | |
(local.tee $error-addr (i32.mul (local.get $y) (i32.const 12))))) | |
(local.set $error-g (f32.load offset=0x5004 (local.get $error-addr))) | |
(local.set $error-r (f32.load offset=0x5008 (local.get $error-addr))) | |
(local.set $b | |
(call $clamp01 | |
(f32.add | |
(call $0-255->0-1 (i32.and (i32.shr_u (local.get $color) (i32.const 16)) (i32.const 255))) | |
(local.get $error-b)))) | |
(local.set $g | |
(call $clamp01 | |
(f32.add | |
(call $0-255->0-1 (i32.and (i32.shr_u (local.get $color) (i32.const 8)) (i32.const 255))) | |
(local.get $error-g)))) | |
(local.set $r | |
(call $clamp01 | |
(f32.add | |
(call $0-255->0-1 (i32.and (i32.shr_u (local.get $color) (i32.const 0)) (i32.const 255))) | |
(local.get $error-r)))) | |
;; second result from $closest-color | |
(local.set $palidx | |
(call $closest-color | |
(i32.or | |
(i32.or | |
(i32.shl (call $0-1->0-255 (local.get $r)) (i32.const 16)) | |
(i32.shl (call $0-1->0-255 (local.get $g)) (i32.const 8))) | |
(call $0-1->0-255 (local.get $b))))) | |
(local.set $new-color (i32.load offset=4 (local.get $palidx))) | |
(local.set $error-b | |
(f32.sub | |
(local.get $b) | |
(call $0-255->0-1 | |
(i32.and (local.get $new-color) (i32.const 255))))) | |
(local.set $error-g | |
(f32.sub | |
(local.get $g) | |
(call $0-255->0-1 | |
(i32.and (i32.shr_u (local.get $new-color) (i32.const 8)) (i32.const 255))))) | |
(local.set $error-r | |
(f32.sub | |
(local.get $r) | |
(call $0-255->0-1 | |
(i32.and (i32.shr_u (local.get $new-color) (i32.const 16)) (i32.const 255))))) | |
;; propagate error to neighbors | |
;; x,y+1 => 7/16 | |
(f32.store offset=0x500c (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x500c (local.get $error-addr)) | |
(f32.mul (local.get $error-b) (f32.const 0.4375)))) | |
(f32.store offset=0x5010 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x5010 (local.get $error-addr)) | |
(f32.mul (local.get $error-g) (f32.const 0.4375)))) | |
(f32.store offset=0x5014 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x5014 (local.get $error-addr)) | |
(f32.mul (local.get $error-r) (f32.const 0.4375)))) | |
;; x+1,y-1 => 3/16 | |
(f32.store offset=0x578c (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x578c (local.get $error-addr)) | |
(f32.mul (local.get $error-b) (f32.const 0.1875)))) | |
(f32.store offset=0x5790 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x5790 (local.get $error-addr)) | |
(f32.mul (local.get $error-g) (f32.const 0.1875)))) | |
(f32.store offset=0x5794 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x5794 (local.get $error-addr)) | |
(f32.mul (local.get $error-r) (f32.const 0.1875)))) | |
;; x+1,y => 5/16 | |
(f32.store offset=0x5798 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x5798 (local.get $error-addr)) | |
(f32.mul (local.get $error-b) (f32.const 0.3175)))) | |
(f32.store offset=0x579c (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x579c (local.get $error-addr)) | |
(f32.mul (local.get $error-g) (f32.const 0.3175)))) | |
(f32.store offset=0x57a0 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x57a0 (local.get $error-addr)) | |
(f32.mul (local.get $error-r) (f32.const 0.3175)))) | |
;; x+1,y+1 => 1/16 | |
(f32.store offset=0x57a4 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x57a4 (local.get $error-addr)) | |
(f32.mul (local.get $error-b) (f32.const 0.0625)))) | |
(f32.store offset=0x57a8 (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x57a8 (local.get $error-addr)) | |
(f32.mul (local.get $error-g) (f32.const 0.0625)))) | |
(f32.store offset=0x57ac (local.get $error-addr) | |
(f32.add | |
(f32.load offset=0x57ac (local.get $error-addr)) | |
(f32.mul (local.get $error-r) (f32.const 0.0625)))) | |
;; write pixel | |
(i32.store8 offset=0xa0 | |
(local.tee $addr | |
(i32.add | |
(i32.mul (local.get $y) (i32.const 40)) | |
(i32.shr_u | |
(local.get $x) | |
(i32.const 2)))) | |
(i32.or | |
(i32.and | |
(i32.load8_u offset=0xa0 (local.get $addr)) | |
(i32.xor | |
(i32.shl | |
(i32.const 3) | |
(local.tee $shift | |
(i32.shl | |
(i32.and | |
(local.get $x) | |
(i32.const 3)) | |
(i32.const 1)))) | |
(i32.const 255))) | |
(i32.shl | |
(i32.shr_u | |
(local.get $palidx) | |
(i32.const 2)) | |
(local.get $shift)))) | |
) | |
;; Changes the rotation speed or movement speed fluidly given an input value. | |
;; $input-addr: Address of 2 bytes of input (either left/right or up/down) | |
;; $value-addr: The value to modify (either rotation or movement speed) | |
;; Returns the new value. | |
(func $move (param $input-bit i32) (param $value-addr i32) (result f32) | |
(local $input i32) | |
(local $result f32) | |
(local.set $input (i32.load8_u (i32.const 0x16))) | |
(f32.store (local.get $value-addr) | |
(local.tee $result | |
(f32.mul | |
(f32.add | |
(f32.load (local.get $value-addr)) | |
(f32.mul | |
(f32.convert_i32_s | |
(i32.sub | |
(i32.and | |
(i32.shr_u (local.get $input) | |
(local.get $input-bit)) | |
(i32.const 1)) | |
(i32.and | |
(i32.shr_u (local.get $input) | |
(i32.add (local.get $input-bit) (i32.const 1))) | |
(i32.const 1)))) | |
(f32.const 0.0078125))) | |
(f32.const 0.875)))) | |
(local.get $result)) | |
(func $text-time (param $time f32) (param $y i32) | |
(local $i i32) | |
(local $itime i32) | |
(local.set $itime | |
(if (result i32) | |
(f32.lt (local.get $time) (f32.const 1000)) | |
(then | |
(i32.trunc_f32_u (f32.mul (local.get $time) (f32.const 100)))) | |
(else | |
(i32.const 99999)))) | |
(local.set $i (i32.const 0x4fff)) | |
(loop $loop | |
(local.set $i (i32.sub (local.get $i) (i32.const 1))) | |
(i32.store8 | |
(local.get $i) | |
(if (result i32) | |
(i32.eq (local.get $i) (i32.const 0x4ffc)) | |
(then | |
(i32.const 46)) ;; ord('.') | |
(else | |
(i32.add (i32.rem_u (local.get $itime) (i32.const 10)) | |
(i32.const 48)) ;; 'ord('0') | |
(local.set $itime (i32.div_u (local.get $itime) (i32.const 10)))))) | |
(br_if $loop (i32.gt_u (local.get $i) (i32.const 0x4ffb))) | |
(br_if $loop (local.get $itime))) | |
(call $text | |
(local.get $i) | |
(i32.sub | |
(i32.const 149) | |
(i32.mul (i32.sub (i32.const 0x4ffe) (local.get $i)) (i32.const 8))) | |
(local.get $y))) | |
(func (export "update") | |
(local $color i32) | |
(local $src i32) | |
(local $dst i32) | |
(local $count i32) | |
(local $d-src i32) | |
(local $byte i32) | |
(local $cells i32) | |
(local $i i32) | |
(local $cell0 i32) | |
(local $cell1 i32) | |
(local $wall-addr i32) | |
(local $dest-wall-addr i32) | |
(local $walls i32) | |
(local $x i32) | |
(local $y i32) | |
(local $640y i32) | |
(local $tex i32) | |
(local $pal i32) | |
(local $ihalf-height i32) | |
(local $dist-index i32) | |
(local $factor f32) | |
(local $xproj f32) | |
(local $Dx f32) | |
(local $Dy f32) | |
(local $dist f32) | |
(local $speed f32) | |
(local $normal-x f32) | |
(local $normal-y f32) | |
(local $wall-scale f32) | |
(local $dot-product f32) | |
(local $half-height f32) | |
(local $u f32) | |
(local $v f32) | |
(local $dv f32) | |
(local $ydv f32) | |
;; Set both $ray-x/$Dx and $ray-y $Dy. | |
;; $Dx/$Dy is used for the view direction, and $ray-x/$ray-y is used for | |
;; the movement vector. | |
(global.set $ray-x | |
(local.tee $Dx | |
(call $sin (global.get $angle)))) | |
(global.set $ray-y | |
(local.tee $Dy | |
(call $sin (f32.add (global.get $angle) (f32.const 1.5707963267948966))))) | |
;; Always decrement the mode timer. | |
(global.set $mode-timer (i32.sub (global.get $mode-timer) (i32.const 1))) | |
(block $done | |
(block $winning | |
(block $game | |
(block $reset | |
(block $wait | |
(block $init | |
(br_table $init $wait $reset $game $winning (global.get $mode))) | |
;; MODE: $init | |
(loop $loop | |
;; initialize distance table: | |
;; 80 / (y + 1) for y in [0, 80) | |
(f32.store offset=0x244c | |
(i32.shl (local.get $y) (i32.const 2)) | |
(local.tee $factor | |
(f32.div | |
(global.get $half-screen-height) | |
(f32.add | |
(f32.convert_i32_s (local.get $y)) | |
(global.get $one))))) | |
;; Make the brightness falloff more slowly. | |
(local.set $factor (f32.sqrt (local.get $factor))) | |
;; Initialize the palette tables with darker versions of all 16 | |
;; colors, for each of the 80 distance values. | |
(loop $color-loop | |
;; Skip the original 64 colors when writing. | |
(i32.store8 offset=0x2b8c ;; 0x2b4c + 0x40 | |
(local.get $color) | |
;; Set $channel to 0xff by default (for alpha). Only adjust | |
;; brightness for RGB channels. | |
(select | |
;; non-alpha channel | |
(i32.trunc_f32_s | |
(f32.div | |
(f32.convert_i32_s | |
;; Mask off the low 6 bits to get the original color. | |
(i32.load8_u offset=0x2b4c | |
(i32.and (local.get $color) (i32.const 63)))) | |
(local.get $factor))) | |
;; alpha channel | |
(i32.const 0xff) | |
(i32.ne (i32.and (local.get $color) (i32.const 3)) (i32.const 3)))) | |
(br_if $color-loop | |
(i32.and | |
(local.tee $color (i32.add (local.get $color) (i32.const 1))) | |
(i32.const 63)))) | |
(br_if $loop | |
(i32.lt_s | |
(local.tee $y (i32.add (local.get $y) (i32.const 1))) | |
(i32.const 80)))) | |
;; Decompress RLE-encoded 2bpp textures | |
;; RLE is encoded as: | |
;; v**n => (+n, v) | |
;; v1,v2,..,vn => (-n, v1, v2,..,vn) | |
;; | |
;; Where each cell is one byte. | |
(local.set $dst (i32.const 0x1c48)) ;; 0x1c4c - 4 | |
(loop $src-loop | |
(if (local.tee $d-src | |
(i32.le_s | |
(local.tee $count (i32.load8_s offset=0x262c (local.get $src))) | |
(i32.const 0))) | |
(then | |
;; -$count singleton elements. | |
(local.set $count (i32.sub (i32.const 0) (local.get $count)))) | |
(else | |
;; Run of length $count. | |
(local.set $src (i32.add (local.get $src) (i32.const 1))))) | |
;; Write the run. | |
(loop $dst-loop | |
;; Each byte is 2bpp, unpack into 8bpp palette index. | |
(i32.store | |
(local.tee $dst (i32.add (local.get $dst) (i32.const 4))) | |
(i32.and | |
(i32.or | |
(i32.or | |
(i32.or | |
(local.tee $byte | |
(i32.load8_u offset=0x262c | |
(local.tee $src | |
(i32.add (local.get $src) (local.get $d-src))))) | |
(i32.shl (local.get $byte) (i32.const 6))) | |
(i32.shl (local.get $byte) (i32.const 12))) | |
(i32.shl (local.get $byte) (i32.const 18))) | |
(i32.const 0x03030303))) | |
(br_if $dst-loop | |
(local.tee $count (i32.sub (local.get $count) (i32.const 1))))) | |
(br_if $src-loop | |
(i32.lt_s | |
(local.tee $src (i32.add (local.get $src) (i32.const 1))) | |
(i32.const 0x100)))) | |
(global.set $mode (i32.const 1)) ;; wait | |
(br $done)) | |
;; MODE: $wait | |
(if (i32.eqz (global.get $mode-timer)) | |
(then | |
(global.set $mode (i32.const 4)) ;; winning | |
(global.set $mode-timer (i32.const 80)))) ;; reset position over time | |
(br $done)) | |
;; MODE: $reset | |
;; clear rotation and movement speed | |
(i64.store align=4 (i32.const 0x19a0) (i64.const 0)) | |
;; Generate maze using Kruskal's algorithm. | |
;; See http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm | |
;; Pack the following values: (i, i + 1, i, i + 12) | |
;; This allows us to use i32.store16 below to write a horizontal or | |
;; vertical wall. | |
(local.set $cells (i32.const 0x0c_00_01_00)) | |
;; start at 0x1a3c - 2 and pre-increment before storing | |
(local.set $wall-addr (i32.const 0x1a3a)) | |
(loop $loop | |
;; Each cell is "owned" by itself at the start. | |
(i32.store8 offset=0x19ac | |
(i32.and (local.get $cells) (i32.const 0xff)) (local.get $cells)) | |
;; Add horizontal edge, connecting cell i and i + 1. | |
(if (i32.lt_s (i32.rem_s (local.get $i) (i32.const 12)) (i32.const 11)) | |
(then | |
(i32.store16 | |
(local.tee $wall-addr (i32.add (local.get $wall-addr) (i32.const 2))) | |
(local.get $cells)))) | |
;; add vertical edge, connecting cell i and i + 12. | |
(if (i32.lt_s (i32.div_s (local.get $i) (i32.const 12)) (i32.const 11)) | |
(then | |
(i32.store16 | |
(local.tee $wall-addr (i32.add (local.get $wall-addr) (i32.const 2))) | |
(i32.shr_u (local.get $cells) (i32.const 16))))) | |
;; increment cell indexes. | |
(local.set $cells (i32.add (local.get $cells) (i32.const 0x01_01_01_01))) | |
(br_if $loop | |
(i32.lt_s | |
(local.tee $i (i32.add (local.get $i) (i32.const 1))) | |
(i32.const 144)))) ;; 12 * 12 | |
(local.set $walls (i32.const 264)) ;; 12 * 11 * 2 | |
(loop $wall-loop | |
;; if each side of the wall is not part of the same set: | |
(if | |
(i32.ne | |
;; $cell0 is the left/up cell. | |
(local.tee $cell0 | |
(i32.load8_u offset=0x19ac | |
(i32.load8_u offset=0x1a3c | |
;; randomly choose a wall | |
(local.tee $wall-addr | |
(i32.shl | |
(i32.trunc_f32_s | |
(f32.mul | |
(call $random) | |
(f32.convert_i32_s (local.get $walls)))) | |
(i32.const 1)))))) | |
;; $cell1 is the right/down cell | |
(local.tee $cell1 | |
(i32.load8_u offset=0x19ac | |
(i32.load8_u offset=0x1a3d (local.get $wall-addr))))) | |
(then | |
;; remove this wall by copying the last wall over it. | |
(i32.store16 offset=0x1a3c | |
(local.get $wall-addr) | |
(i32.load16_u offset=0x1a3c | |
(i32.shl | |
(local.tee $walls (i32.sub (local.get $walls) (i32.const 1))) | |
(i32.const 1)))) | |
;; replace all cells that contain $cell1 with $cell0. | |
;; loop over range [0x0090,0x0000), so use an offset of 0x19ab so | |
;; the stored addresses are in the range (0x1a3c,0x19ac]. | |
(local.set $i (i32.const 0x0090)) | |
(loop $remove-loop | |
(if (i32.eq | |
(i32.load8_u offset=0x19ab (local.get $i)) | |
(local.get $cell1)) | |
(then | |
(i32.store8 offset=0x19ab (local.get $i) (local.get $cell0)))) | |
(br_if $remove-loop | |
(local.tee $i (i32.sub (local.get $i) (i32.const 1))))))) | |
;; loop until there are exactly 11 * 11 walls. | |
(br_if $wall-loop (i32.gt_s (local.get $walls) (i32.const 121)))) | |
;; generate walls for use in-game. | |
(local.set $wall-addr (i32.const 0x1a3c)) | |
(local.set $dest-wall-addr (i32.const 0x2770)) ;; 0x2778 - 8 | |
(loop $wall-loop | |
;; Store the x,y coordinate of the wall, given the cell index. | |
;; Multiply by 2 so each cell is 2x2 units. | |
(i32.store16 | |
;; Increment $dest-wall-addr early, so we can use local.tee instead of | |
;; local.set. To do allow this, we have to start at 0x0ddc - 8 (see | |
;; above). | |
(local.tee $dest-wall-addr | |
(i32.add (local.get $dest-wall-addr) (i32.const 8))) | |
(i32.shl | |
(i32.or | |
(i32.shl | |
(i32.div_s | |
;; Save the right/bottom cell of the wall as $i. | |
(local.tee $i (i32.load8_u offset=1 (local.get $wall-addr))) | |
(i32.const 12)) | |
(i32.const 8)) | |
(i32.rem_s (local.get $i) (i32.const 12))) | |
(i32.const 1))) | |
(i64.store offset=2 align=2 | |
(local.get $dest-wall-addr) | |
(select | |
;; left-right wall | |
;; Write dx=0, dy=2. We can use an unaligned write to combine this with | |
;; updating pal, tex, and scale too. | |
;; This ends up writing: | |
;; \00 ;; dx | |
;; \02 ;; dy | |
;; \02\01\18\00 ;; scale:2, tex:1, pal:6<<2 | |
(i64.const 0x18_01_02_02_00) | |
;; top-bottom wall | |
;; \02 ;; dx | |
;; \00 ;; dy | |
;; \02\01\14\00 ;; scale:2, tex:1, pal:5<<2 | |
(i64.const 0x14_01_02_00_02) | |
;; Get the two cells of the wall. If the difference is 1, it must be | |
;; left/right. | |
(i32.eq | |
(i32.sub (local.get $i) (i32.load8_u (local.get $wall-addr))) | |
(i32.const 1)))) | |
(br_if $wall-loop | |
(i32.lt_s | |
(local.tee $wall-addr (i32.add (local.get $wall-addr) (i32.const 2))) | |
(i32.const 0x1b2e)))) ;; 0x1a3c + 11 * 11 * 2 | |
(global.set $max-wall-addr (i32.const 0x2b40)) | |
(global.set $mode (i32.const 3)) ;; game | |
(global.set $timer-running (i32.const 1)) | |
(global.set $time (f32.const 0)) | |
(br $done)) | |
;; MODE: $game | |
;; add 1/60 to time | |
(if (global.get $timer-running) | |
(then | |
(global.set $time | |
(f32.add | |
(global.get $time) | |
(f32.const 0.016666666666666666))))) | |
;; Rotate if left or right is pressed. | |
(global.set $angle | |
(f32.add | |
(global.get $angle) | |
(call $move (i32.const 0x0004) (i32.const 0x19a0)))) | |
;; angle = fmod(angle, 2 * pi) | |
(global.set $angle | |
(f32.sub | |
(global.get $angle) | |
(f32.mul | |
(f32.trunc | |
(f32.div | |
(global.get $angle) | |
(f32.const 6.283185307179586))) | |
(f32.const 6.283185307179586)))) | |
;; Move forward if up is pressed. | |
;; If the speed is negative, flip the movement vector | |
(if (f32.lt | |
(local.tee $speed (call $move (i32.const 0x0006) (i32.const 0x19a4))) | |
(global.get $zero)) | |
(then | |
(local.set $speed (f32.neg (local.get $speed))) | |
(global.set $ray-x (f32.neg (global.get $ray-x))) | |
(global.set $ray-y (f32.neg (global.get $ray-y))))) | |
;; Move if the speed is non-zero. | |
(if (f32.gt (local.get $speed) (global.get $zero)) | |
(then | |
;; Try to move, but stop at the nearest wall. | |
;; Afterward, $dist is the distance to the wall. | |
(global.set $Px | |
(f32.add | |
(global.get $Px) | |
(f32.mul | |
(global.get $ray-x) | |
(local.tee $dist | |
(f32.min | |
;; Epsilon to prevent landing on the wall. | |
(f32.add (call $ray-walls) (f32.const 0.001953125)) | |
(local.get $speed)))))) | |
(global.set $Py | |
(f32.add | |
(global.get $Py) | |
(f32.mul (global.get $ray-y) (local.get $dist)))) | |
;; Store the dot product of the normal and the vector to P, to see if | |
;; the normal is pointing in the right direction. | |
(local.set $dot-product | |
(f32.add | |
(f32.mul | |
;; Store the normal of the nearest wall. | |
;; Wall is stored as (x,y),(dx,dy),scale. | |
;; Since we want the normal, store (-dy/scale, dx/scale). | |
(local.tee $normal-x | |
(f32.neg | |
(f32.div | |
(f32.convert_i32_s | |
(i32.load8_s offset=3 (global.get $min-wall))) | |
(local.tee $wall-scale | |
(f32.convert_i32_u | |
(i32.load8_u offset=4 (global.get $min-wall))))))) | |
(f32.sub | |
(global.get $Px) | |
(f32.convert_i32_s | |
(i32.load8_s (global.get $min-wall))))) | |
(f32.mul | |
(local.tee $normal-y | |
(f32.div | |
(f32.convert_i32_s | |
(i32.load8_s offset=2 (global.get $min-wall))) | |
(local.get $wall-scale))) | |
(f32.sub | |
(global.get $Py) | |
(f32.convert_i32_s | |
(i32.load8_s offset=1 (global.get $min-wall))))))) | |
;; Push the player away from the wall if they're too close. Since the | |
;; $dot-product is signed, we need to use the absolute value to find | |
;; the actual distance. | |
(if (f32.gt | |
(local.tee $dist | |
(f32.sub (f32.const 0.25) (f32.abs (local.get $dot-product)))) | |
(global.get $zero)) | |
(then | |
(global.set $Px | |
(f32.add | |
(global.get $Px) | |
(f32.mul | |
(local.get $normal-x) | |
(local.tee $dist | |
;; Use the sign of the $dot-product on the positive value | |
;; $dist to push in the proper direction. | |
(f32.copysign (local.get $dist) (local.get $dot-product)))))) | |
(global.set $Py | |
(f32.add | |
(global.get $Py) | |
(f32.mul (local.get $normal-y) (local.get $dist)))))))) | |
;; If the player reaches the goal, generate a new maze, and reset their | |
;; position. | |
(if (i32.and | |
(f32.gt (global.get $Px) (f32.convert_i32_s (i32.const 22))) | |
(f32.gt (global.get $Py) (f32.convert_i32_s (i32.const 22)))) | |
(then | |
;; TODO | |
;; (call $timer (i32.const 0)) ;; stop timer | |
(global.set $mode (i32.const 4)) ;; winning | |
(global.set $mode-timer (i32.const 80)) ;; reset position over time | |
(global.set $max-wall-addr (i32.const 0x2778)) | |
(global.set $timer-running (i32.const 0)) | |
(global.set $best-time | |
(f32.min (global.get $time) (global.get $best-time))))) | |
(br $done)) | |
;; MODE: $winning | |
;; Move the player back to the beginning. | |
(global.set $Px | |
(f32.add | |
(global.get $one-half) | |
(f32.mul | |
(f32.sub (global.get $Px) (global.get $one-half)) | |
(local.tee $dist | |
(f32.div | |
(f32.convert_i32_s (global.get $mode-timer)) | |
(global.get $half-screen-height)))))) | |
(global.set $Py | |
(f32.add | |
(global.get $one-half) | |
(f32.mul | |
(f32.sub (global.get $Py) (global.get $one-half)) (local.get $dist)))) | |
(global.set $angle | |
(f32.add | |
(f32.const 0.7853981633974483) | |
(f32.mul | |
(f32.sub (global.get $angle) (f32.const 0.7853981633974483)) | |
(local.get $dist)))) | |
(if (i32.eqz (global.get $mode-timer)) | |
(then | |
(global.set $mode (i32.const 2)) ;; reset | |
(global.set $mode-timer (i32.const 15))))) ;; shorter wait | |
;; DRAWING: | |
(local.set $i (i32.const 0)) | |
(loop $loop | |
;; clear error in first column | |
(f32.store offset=0x578c (local.get $i) (f32.const 0)) | |
(br_if $loop | |
(i32.lt_u | |
(local.tee $i (i32.add (local.get $i) (i32.const 4))) | |
(i32.const 0x780)))) | |
;; Loop for each column. | |
(loop $x-loop | |
;; clear/copy error | |
(local.set $i (i32.const 0)) | |
(loop $loop | |
;; copy error from next column | |
(f32.store offset=0x5000 (local.get $i) (f32.load offset=0x578c (local.get $i))) | |
;; clear error in next column | |
(f32.store offset=0x578c (local.get $i) (f32.const 0)) | |
(br_if $loop | |
(i32.lt_u | |
(local.tee $i (i32.add (local.get $i) (i32.const 4))) | |
(i32.const 0x78c)))) | |
;; Shoot a ray against a wall. Use rays projected onto screen plane. | |
(global.set $ray-x | |
(f32.add | |
(local.get $Dx) | |
(f32.mul | |
(local.tee $xproj | |
(f32.div | |
(f32.convert_i32_s (i32.sub (local.get $x) (i32.const 80))) | |
(f32.convert_i32_s (i32.const 120)))) | |
(f32.neg (local.get $Dy))))) | |
(global.set $ray-y | |
(f32.add (local.get $Dy) (f32.mul (local.get $xproj) (local.get $Dx)))) | |
;; Draw a vertical strip of the scene, including ceiling, wall, and floor. | |
;; Fire the ray, and find the closest wall that is hit. Divide the | |
;; half-screen-height (80 pixels) by this distance to produce the | |
;; half-height for this wall, but clamp it so 80 we don't access | |
;; out-of-bounds. | |
(if (i32.ge_s | |
(local.tee $ihalf-height | |
(i32.trunc_f32_s | |
(local.tee $half-height | |
(f32.div (global.get $half-screen-height) (call $ray-walls))))) | |
(i32.const 80)) | |
(then | |
(local.set $ihalf-height (i32.const 80)))) | |
;; Loop over all pixels in this column. | |
;; ceiling pixels | |
(local.set $y (i32.const 0)) | |
(if (i32.lt_s (local.get $ihalf-height) (i32.const 80)) | |
(then | |
(local.set $tex (i32.const 0)) | |
(local.set $pal (i32.const 0x8)) | |
(loop $y-loop | |
;; Find UV using distance table | |
(local.set $dist-index (i32.sub (i32.const 80) (local.get $y))) | |
(local.set $u | |
(f32.add | |
(global.get $Px) | |
(f32.mul | |
(global.get $ray-x) | |
(local.tee $dist | |
(f32.load offset=0x244c | |
(i32.shl (local.get $dist-index) (i32.const 2))))))) | |
(local.set $v | |
(f32.add | |
(global.get $Py) | |
(f32.mul (global.get $ray-y) (local.get $dist)))) | |
(call $pixel | |
(local.get $x) | |
(local.get $y) | |
(call $texture | |
(local.get $tex) (local.get $pal) (local.get $dist-index) | |
(local.get $u) (local.get $v))) | |
(br_if $y-loop | |
(i32.lt_s | |
(local.tee $y (i32.add (local.get $y) (i32.const 1))) | |
(i32.sub | |
(i32.const 80) | |
(local.get $ihalf-height))))))) | |
;; wall | |
(if (i32.gt_s (local.get $ihalf-height) (i32.const 0)) | |
(then | |
(local.set $dist-index (local.get $ihalf-height)) | |
(local.set $u (global.get $min-t2)) | |
(local.set $dv (f32.div (global.get $one-half) (local.get $half-height))) | |
(local.set $tex (i32.load8_u offset=5 (global.get $min-wall))) | |
(local.set $pal (i32.load8_u offset=6 (global.get $min-wall))) | |
(loop $y-loop | |
(local.set $v | |
(f32.add | |
(global.get $one-half) | |
(local.tee $ydv | |
(f32.mul (f32.convert_i32_s (i32.sub (i32.const 80) (local.get $y))) (local.get $dv))))) | |
(call $pixel | |
(local.get $x) | |
(local.get $y) | |
(call $texture | |
(local.get $tex) (local.get $pal) (local.get $dist-index) | |
(local.get $u) (local.get $v))) | |
(br_if $y-loop | |
(i32.lt_s | |
(local.tee $y (i32.add (local.get $y) (i32.const 1))) | |
(i32.add | |
(i32.const 80) | |
(local.get $ihalf-height))))))) | |
;; floor | |
(if (i32.lt_s (local.get $ihalf-height) (i32.const 80)) | |
(then | |
(local.set $tex (i32.const 1)) | |
(local.set $pal (i32.const 0xc)) | |
(loop $y-loop | |
;; Find UV using distance table | |
(local.set $dist-index (i32.sub (local.get $y) (i32.const 80))) | |
(local.set $u | |
(f32.add | |
(global.get $Px) | |
(f32.mul | |
(global.get $ray-x) | |
(local.tee $dist | |
(f32.load offset=0x244c | |
(i32.shl (local.get $dist-index) (i32.const 2))))))) | |
(local.set $v | |
(f32.add | |
(global.get $Py) | |
(f32.mul (global.get $ray-y) (local.get $dist)))) | |
(call $pixel | |
(local.get $x) | |
(local.get $y) | |
(call $texture | |
(local.get $tex) (local.get $pal) (local.get $dist-index) | |
(local.get $u) (local.get $v))) | |
(br_if $y-loop | |
(i32.lt_s | |
(local.tee $y (i32.add (local.get $y) (i32.const 1))) | |
(i32.const 160)))))) | |
;; loop on x | |
(br_if $x-loop | |
(i32.lt_s | |
(local.tee $x (i32.add (local.get $x) (i32.const 1))) | |
(i32.const 160)))) | |
(call $text-time (global.get $time) (i32.const 3)) | |
(call $text-time (global.get $best-time) (i32.const 14))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment