Initial import.
authorThomas Pornin <pornin@bolet.org>
Wed, 2 Nov 2016 23:01:13 +0000 (19:01 -0400)
committerThomas Pornin <pornin@bolet.org>
Wed, 2 Nov 2016 23:01:13 +0000 (19:01 -0400)
292 files changed:
LICENSE.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.txt [new file with mode: 0644]
T0/BlobWriter.cs [new file with mode: 0644]
T0/CPU.cs [new file with mode: 0644]
T0/CodeElement.cs [new file with mode: 0644]
T0/CodeElementJump.cs [new file with mode: 0644]
T0/CodeElementUInt.cs [new file with mode: 0644]
T0/CodeElementUIntExpr.cs [new file with mode: 0644]
T0/CodeElementUIntInt.cs [new file with mode: 0644]
T0/CodeElementUIntUInt.cs [new file with mode: 0644]
T0/ConstData.cs [new file with mode: 0644]
T0/Opcode.cs [new file with mode: 0644]
T0/OpcodeCall.cs [new file with mode: 0644]
T0/OpcodeConst.cs [new file with mode: 0644]
T0/OpcodeGetLocal.cs [new file with mode: 0644]
T0/OpcodeJump.cs [new file with mode: 0644]
T0/OpcodeJumpIf.cs [new file with mode: 0644]
T0/OpcodeJumpIfNot.cs [new file with mode: 0644]
T0/OpcodeJumpUncond.cs [new file with mode: 0644]
T0/OpcodePutLocal.cs [new file with mode: 0644]
T0/OpcodeRet.cs [new file with mode: 0644]
T0/SType.cs [new file with mode: 0644]
T0/T0Comp.cs [new file with mode: 0644]
T0/TPointerBase.cs [new file with mode: 0644]
T0/TPointerBlob.cs [new file with mode: 0644]
T0/TPointerExpr.cs [new file with mode: 0644]
T0/TPointerNull.cs [new file with mode: 0644]
T0/TPointerXT.cs [new file with mode: 0644]
T0/TValue.cs [new file with mode: 0644]
T0/Word.cs [new file with mode: 0644]
T0/WordBuilder.cs [new file with mode: 0644]
T0/WordData.cs [new file with mode: 0644]
T0/WordInterpreted.cs [new file with mode: 0644]
T0/WordNative.cs [new file with mode: 0644]
T0/kern.t0 [new file with mode: 0644]
T0Comp.exe [new file with mode: 0755]
build/.do_not_remove [new file with mode: 0644]
inc/bearssl.h [new file with mode: 0644]
inc/bearssl_block.h [new file with mode: 0644]
inc/bearssl_ec.h [new file with mode: 0644]
inc/bearssl_hash.h [new file with mode: 0644]
inc/bearssl_hmac.h [new file with mode: 0644]
inc/bearssl_pem.h [new file with mode: 0644]
inc/bearssl_prf.h [new file with mode: 0644]
inc/bearssl_rand.h [new file with mode: 0644]
inc/bearssl_rsa.h [new file with mode: 0644]
inc/bearssl_ssl.h [new file with mode: 0644]
inc/bearssl_x509.h [new file with mode: 0644]
mkT0.sh [new file with mode: 0755]
samples/README.txt [new file with mode: 0644]
samples/cert-ee-ec+rsa.pem [new file with mode: 0644]
samples/cert-ee-ec.pem [new file with mode: 0644]
samples/cert-ee-rsa.pem [new file with mode: 0644]
samples/cert-ica-ec.pem [new file with mode: 0644]
samples/cert-ica-rsa.pem [new file with mode: 0644]
samples/cert-root-ec.pem [new file with mode: 0644]
samples/cert-root-rsa.pem [new file with mode: 0644]
samples/chain-ec+rsa.h [new file with mode: 0644]
samples/chain-ec.h [new file with mode: 0644]
samples/chain-rsa.h [new file with mode: 0644]
samples/client_basic.c [new file with mode: 0644]
samples/custom_profile.c [new file with mode: 0644]
samples/key-ec.h [new file with mode: 0644]
samples/key-ee-ec.pem [new file with mode: 0644]
samples/key-ee-rsa.pem [new file with mode: 0644]
samples/key-ica-ec.pem [new file with mode: 0644]
samples/key-ica-rsa.pem [new file with mode: 0644]
samples/key-root-ec.pem [new file with mode: 0644]
samples/key-root-rsa.pem [new file with mode: 0644]
samples/key-rsa.h [new file with mode: 0644]
samples/server_basic.c [new file with mode: 0644]
src/codec/ccopy.c [new file with mode: 0644]
src/codec/dec16be.c [new file with mode: 0644]
src/codec/dec16le.c [new file with mode: 0644]
src/codec/dec32be.c [new file with mode: 0644]
src/codec/dec32le.c [new file with mode: 0644]
src/codec/dec64be.c [new file with mode: 0644]
src/codec/dec64le.c [new file with mode: 0644]
src/codec/enc16be.c [new file with mode: 0644]
src/codec/enc16le.c [new file with mode: 0644]
src/codec/enc32be.c [new file with mode: 0644]
src/codec/enc32le.c [new file with mode: 0644]
src/codec/enc64be.c [new file with mode: 0644]
src/codec/enc64le.c [new file with mode: 0644]
src/codec/pemdec.c [new file with mode: 0644]
src/codec/pemdec.t0 [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/ec/ec_prime_i31.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp256r1.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp384r1.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp521r1.c [new file with mode: 0644]
src/ec/ec_secp256r1.c [new file with mode: 0644]
src/ec/ec_secp384r1.c [new file with mode: 0644]
src/ec/ec_secp521r1.c [new file with mode: 0644]
src/ec/ecdsa_atr.c [new file with mode: 0644]
src/ec/ecdsa_i31_bits.c [new file with mode: 0644]
src/ec/ecdsa_i31_sign_asn1.c [new file with mode: 0644]
src/ec/ecdsa_i31_sign_raw.c [new file with mode: 0644]
src/ec/ecdsa_i31_vrfy_asn1.c [new file with mode: 0644]
src/ec/ecdsa_i31_vrfy_raw.c [new file with mode: 0644]
src/ec/ecdsa_rta.c [new file with mode: 0644]
src/hash/dig_oid.c [new file with mode: 0644]
src/hash/dig_size.c [new file with mode: 0644]
src/hash/ghash_ctmul.c [new file with mode: 0644]
src/hash/ghash_ctmul32.c [new file with mode: 0644]
src/hash/ghash_ctmul64.c [new file with mode: 0644]
src/hash/md5.c [new file with mode: 0644]
src/hash/md5sha1.c [new file with mode: 0644]
src/hash/multihash.c [new file with mode: 0644]
src/hash/sha1.c [new file with mode: 0644]
src/hash/sha2big.c [new file with mode: 0644]
src/hash/sha2small.c [new file with mode: 0644]
src/inner.h [new file with mode: 0644]
src/int/i31_add.c [new file with mode: 0644]
src/int/i31_bitlen.c [new file with mode: 0644]
src/int/i31_decmod.c [new file with mode: 0644]
src/int/i31_decode.c [new file with mode: 0644]
src/int/i31_decred.c [new file with mode: 0644]
src/int/i31_encode.c [new file with mode: 0644]
src/int/i31_fmont.c [new file with mode: 0644]
src/int/i31_iszero.c [new file with mode: 0644]
src/int/i31_modpow.c [new file with mode: 0644]
src/int/i31_montmul.c [new file with mode: 0644]
src/int/i31_mulacc.c [new file with mode: 0644]
src/int/i31_muladd.c [new file with mode: 0644]
src/int/i31_ninv31.c [new file with mode: 0644]
src/int/i31_reduce.c [new file with mode: 0644]
src/int/i31_rshift.c [new file with mode: 0644]
src/int/i31_sub.c [new file with mode: 0644]
src/int/i31_tmont.c [new file with mode: 0644]
src/int/i32_add.c [new file with mode: 0644]
src/int/i32_bitlen.c [new file with mode: 0644]
src/int/i32_decmod.c [new file with mode: 0644]
src/int/i32_decode.c [new file with mode: 0644]
src/int/i32_decred.c [new file with mode: 0644]
src/int/i32_div32.c [new file with mode: 0644]
src/int/i32_encode.c [new file with mode: 0644]
src/int/i32_fmont.c [new file with mode: 0644]
src/int/i32_iszero.c [new file with mode: 0644]
src/int/i32_modpow.c [new file with mode: 0644]
src/int/i32_montmul.c [new file with mode: 0644]
src/int/i32_mulacc.c [new file with mode: 0644]
src/int/i32_muladd.c [new file with mode: 0644]
src/int/i32_ninv32.c [new file with mode: 0644]
src/int/i32_reduce.c [new file with mode: 0644]
src/int/i32_sub.c [new file with mode: 0644]
src/int/i32_tmont.c [new file with mode: 0644]
src/mac/hmac.c [new file with mode: 0644]
src/mac/hmac_ct.c [new file with mode: 0644]
src/rand/hmac_drbg.c [new file with mode: 0644]
src/rsa/rsa_i31_pkcs1_sign.c [new file with mode: 0644]
src/rsa/rsa_i31_pkcs1_vrfy.c [new file with mode: 0644]
src/rsa/rsa_i31_priv.c [new file with mode: 0644]
src/rsa/rsa_i31_pub.c [new file with mode: 0644]
src/rsa/rsa_i32_pkcs1_sign.c [new file with mode: 0644]
src/rsa/rsa_i32_pkcs1_vrfy.c [new file with mode: 0644]
src/rsa/rsa_i32_priv.c [new file with mode: 0644]
src/rsa/rsa_i32_pub.c [new file with mode: 0644]
src/rsa/rsa_ssl_decrypt.c [new file with mode: 0644]
src/ssl/prf.c [new file with mode: 0644]
src/ssl/prf_md5sha1.c [new file with mode: 0644]
src/ssl/prf_sha256.c [new file with mode: 0644]
src/ssl/prf_sha384.c [new file with mode: 0644]
src/ssl/ssl_client.c [new file with mode: 0644]
src/ssl/ssl_client_full.c [new file with mode: 0644]
src/ssl/ssl_engine.c [new file with mode: 0644]
src/ssl/ssl_hashes.c [new file with mode: 0644]
src/ssl/ssl_hs_client.c [new file with mode: 0644]
src/ssl/ssl_hs_client.t0 [new file with mode: 0644]
src/ssl/ssl_hs_common.t0 [new file with mode: 0644]
src/ssl/ssl_hs_server.c [new file with mode: 0644]
src/ssl/ssl_hs_server.t0 [new file with mode: 0644]
src/ssl/ssl_io.c [new file with mode: 0644]
src/ssl/ssl_lru.c [new file with mode: 0644]
src/ssl/ssl_rec_cbc.c [new file with mode: 0644]
src/ssl/ssl_rec_gcm.c [new file with mode: 0644]
src/ssl/ssl_server.c [new file with mode: 0644]
src/ssl/ssl_server_full_ec.c [new file with mode: 0644]
src/ssl/ssl_server_full_rsa.c [new file with mode: 0644]
src/ssl/ssl_server_mine2g.c [new file with mode: 0644]
src/ssl/ssl_server_minf2g.c [new file with mode: 0644]
src/ssl/ssl_server_minr2g.c [new file with mode: 0644]
src/ssl/ssl_server_minu2g.c [new file with mode: 0644]
src/ssl/ssl_server_minv2g.c [new file with mode: 0644]
src/ssl/ssl_single_ec.c [new file with mode: 0644]
src/ssl/ssl_single_rsa.c [new file with mode: 0644]
src/symcipher/aes_big_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_big_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_big_ctr.c [new file with mode: 0644]
src/symcipher/aes_big_dec.c [new file with mode: 0644]
src/symcipher/aes_big_enc.c [new file with mode: 0644]
src/symcipher/aes_common.c [new file with mode: 0644]
src/symcipher/aes_ct.c [new file with mode: 0644]
src/symcipher/aes_ct64.c [new file with mode: 0644]
src/symcipher/aes_ct64_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_ct64_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_ct64_ctr.c [new file with mode: 0644]
src/symcipher/aes_ct64_dec.c [new file with mode: 0644]
src/symcipher/aes_ct64_enc.c [new file with mode: 0644]
src/symcipher/aes_ct_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_ct_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_ct_ctr.c [new file with mode: 0644]
src/symcipher/aes_ct_dec.c [new file with mode: 0644]
src/symcipher/aes_ct_enc.c [new file with mode: 0644]
src/symcipher/aes_small_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_small_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_small_ctr.c [new file with mode: 0644]
src/symcipher/aes_small_dec.c [new file with mode: 0644]
src/symcipher/aes_small_enc.c [new file with mode: 0644]
src/symcipher/des_ct.c [new file with mode: 0644]
src/symcipher/des_ct_cbcdec.c [new file with mode: 0644]
src/symcipher/des_ct_cbcenc.c [new file with mode: 0644]
src/symcipher/des_support.c [new file with mode: 0644]
src/symcipher/des_tab.c [new file with mode: 0644]
src/symcipher/des_tab_cbcdec.c [new file with mode: 0644]
src/symcipher/des_tab_cbcenc.c [new file with mode: 0644]
src/x509/asn1.t0 [new file with mode: 0644]
src/x509/skey_decoder.c [new file with mode: 0644]
src/x509/skey_decoder.t0 [new file with mode: 0644]
src/x509/x509_decoder.c [new file with mode: 0644]
src/x509/x509_decoder.t0 [new file with mode: 0644]
src/x509/x509_knownkey.c [new file with mode: 0644]
src/x509/x509_minimal.c [new file with mode: 0644]
src/x509/x509_minimal.t0 [new file with mode: 0644]
test/test_crypto.c [new file with mode: 0644]
test/test_math.c [new file with mode: 0644]
test/test_speed.c [new file with mode: 0644]
test/test_x509.c [new file with mode: 0644]
test/x509/alltests.txt [new file with mode: 0644]
test/x509/dn-ee.der [new file with mode: 0644]
test/x509/dn-ica1.der [new file with mode: 0644]
test/x509/dn-ica2.der [new file with mode: 0644]
test/x509/dn-root.der [new file with mode: 0644]
test/x509/ee-badsig1.crt [new file with mode: 0644]
test/x509/ee-badsig2.crt [new file with mode: 0644]
test/x509/ee-dates.crt [new file with mode: 0644]
test/x509/ee-md5.crt [new file with mode: 0644]
test/x509/ee-names.crt [new file with mode: 0644]
test/x509/ee-names2.crt [new file with mode: 0644]
test/x509/ee-names3.crt [new file with mode: 0644]
test/x509/ee-names4.crt [new file with mode: 0644]
test/x509/ee-p256-sha1.crt [new file with mode: 0644]
test/x509/ee-p256-sha224.crt [new file with mode: 0644]
test/x509/ee-p256-sha256.crt [new file with mode: 0644]
test/x509/ee-p256-sha384.crt [new file with mode: 0644]
test/x509/ee-p256-sha512.crt [new file with mode: 0644]
test/x509/ee-p256.crt [new file with mode: 0644]
test/x509/ee-p384.crt [new file with mode: 0644]
test/x509/ee-p521.crt [new file with mode: 0644]
test/x509/ee-sha1.crt [new file with mode: 0644]
test/x509/ee-sha224.crt [new file with mode: 0644]
test/x509/ee-sha384.crt [new file with mode: 0644]
test/x509/ee-sha512.crt [new file with mode: 0644]
test/x509/ee-trailing.crt [new file with mode: 0644]
test/x509/ee.crt [new file with mode: 0644]
test/x509/ica1-1016.crt [new file with mode: 0644]
test/x509/ica1-1017.crt [new file with mode: 0644]
test/x509/ica1-4096.crt [new file with mode: 0644]
test/x509/ica1-p256.crt [new file with mode: 0644]
test/x509/ica1-p384.crt [new file with mode: 0644]
test/x509/ica1-p521.crt [new file with mode: 0644]
test/x509/ica1.crt [new file with mode: 0644]
test/x509/ica2-1016.crt [new file with mode: 0644]
test/x509/ica2-1017.crt [new file with mode: 0644]
test/x509/ica2-4096.crt [new file with mode: 0644]
test/x509/ica2-notCA.crt [new file with mode: 0644]
test/x509/ica2-p256.crt [new file with mode: 0644]
test/x509/ica2-p384.crt [new file with mode: 0644]
test/x509/ica2-p521.crt [new file with mode: 0644]
test/x509/ica2.crt [new file with mode: 0644]
test/x509/junk.crt [new file with mode: 0644]
test/x509/root-p256.crt [new file with mode: 0644]
test/x509/root-p384.crt [new file with mode: 0644]
test/x509/root-p521.crt [new file with mode: 0644]
test/x509/root.crt [new file with mode: 0644]
tools/brssl.c [new file with mode: 0644]
tools/brssl.h [new file with mode: 0644]
tools/certs.c [new file with mode: 0644]
tools/chain.c [new file with mode: 0644]
tools/client.c [new file with mode: 0644]
tools/errors.c [new file with mode: 0644]
tools/files.c [new file with mode: 0644]
tools/keys.c [new file with mode: 0644]
tools/names.c [new file with mode: 0644]
tools/server.c [new file with mode: 0644]
tools/skey.c [new file with mode: 0644]
tools/sslio.c [new file with mode: 0644]
tools/ta.c [new file with mode: 0644]
tools/vector.c [new file with mode: 0644]
tools/verify.c [new file with mode: 0644]
tools/xmem.c [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..0885020
--- /dev/null
@@ -0,0 +1,21 @@
+Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+
+Permission is hereby granted, free of charge, to any person obtaining 
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be 
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..5eabd31
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,606 @@
+# Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining 
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be 
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+.POSIX:
+
+# ========================================================================
+# Configurable elements: C compiler and flags, linker flags, static
+# library archival command.
+
+CC = gcc
+CFLAGS = -W -Wall -Os -fPIC -I src -I inc
+#CFLAGS = -W -Wall -g -fPIC -I src -I inc
+LDFLAGS = 
+AR = ar -rcs
+
+# Nothing is meant to be changed below this line.
+
+# ========================================================================
+
+HEADERS = inc/bearssl.h inc/bearssl_block.h inc/bearssl_ec.h inc/bearssl_hash.h inc/bearssl_hmac.h inc/bearssl_pem.h inc/bearssl_prf.h inc/bearssl_rand.h inc/bearssl_rsa.h inc/bearssl_ssl.h inc/bearssl_x509.h src/inner.h src/config.h
+BUILD = build
+
+BEARSSLLIB = libbearssl.a
+BRSSL = brssl
+TESTCRYPTO = testcrypto
+TESTSPEED = testspeed
+TESTX509 = testx509
+TESTMATH = testmath
+
+OBJCODEC = $(BUILD)/ccopy.o $(BUILD)/dec16be.o $(BUILD)/dec16le.o $(BUILD)/dec32be.o $(BUILD)/dec32le.o $(BUILD)/dec64be.o $(BUILD)/dec64le.o $(BUILD)/enc16be.o $(BUILD)/enc16le.o $(BUILD)/enc32be.o $(BUILD)/enc32le.o $(BUILD)/enc64be.o $(BUILD)/enc64le.o $(BUILD)/pemdec.o
+OBJEC = $(BUILD)/ec_prime_i31.o $(BUILD)/ec_prime_i31_secp256r1.o $(BUILD)/ec_prime_i31_secp384r1.o $(BUILD)/ec_prime_i31_secp521r1.o $(BUILD)/ec_secp256r1.o $(BUILD)/ec_secp384r1.o $(BUILD)/ec_secp521r1.o $(BUILD)/ecdsa_atr.o $(BUILD)/ecdsa_i31_bits.o $(BUILD)/ecdsa_i31_sign_asn1.o $(BUILD)/ecdsa_i31_sign_raw.o $(BUILD)/ecdsa_i31_vrfy_asn1.o $(BUILD)/ecdsa_i31_vrfy_raw.o $(BUILD)/ecdsa_rta.o
+OBJHASH = $(BUILD)/dig_oid.o $(BUILD)/dig_size.o $(BUILD)/ghash_ctmul.o $(BUILD)/ghash_ctmul32.o $(BUILD)/ghash_ctmul64.o $(BUILD)/md5.o $(BUILD)/md5sha1.o $(BUILD)/multihash.o $(BUILD)/sha1.o $(BUILD)/sha2big.o $(BUILD)/sha2small.o
+OBJINT31 = $(BUILD)/i31_add.o $(BUILD)/i31_bitlen.o $(BUILD)/i31_decmod.o $(BUILD)/i31_decode.o $(BUILD)/i31_decred.o $(BUILD)/i31_encode.o $(BUILD)/i31_fmont.o $(BUILD)/i31_iszero.o $(BUILD)/i31_modpow.o $(BUILD)/i31_montmul.o $(BUILD)/i31_mulacc.o $(BUILD)/i31_muladd.o $(BUILD)/i31_ninv31.o $(BUILD)/i31_reduce.o $(BUILD)/i31_rshift.o $(BUILD)/i31_sub.o $(BUILD)/i31_tmont.o
+OBJINT32 = $(BUILD)/i32_add.o $(BUILD)/i32_bitlen.o $(BUILD)/i32_decmod.o $(BUILD)/i32_decode.o $(BUILD)/i32_decred.o $(BUILD)/i32_div32.o $(BUILD)/i32_encode.o $(BUILD)/i32_fmont.o $(BUILD)/i32_iszero.o $(BUILD)/i32_modpow.o $(BUILD)/i32_montmul.o $(BUILD)/i32_mulacc.o $(BUILD)/i32_muladd.o $(BUILD)/i32_ninv32.o $(BUILD)/i32_reduce.o $(BUILD)/i32_sub.o $(BUILD)/i32_tmont.o
+OBJMAC = $(BUILD)/hmac.o $(BUILD)/hmac_ct.o
+OBJRAND = $(BUILD)/hmac_drbg.o
+OBJRSA = $(BUILD)/rsa_i31_pkcs1_sign.o $(BUILD)/rsa_i31_pkcs1_vrfy.o $(BUILD)/rsa_i31_priv.o $(BUILD)/rsa_i31_pub.o $(BUILD)/rsa_i32_pkcs1_sign.o $(BUILD)/rsa_i32_pkcs1_vrfy.o $(BUILD)/rsa_i32_priv.o $(BUILD)/rsa_i32_pub.o $(BUILD)/rsa_ssl_decrypt.o
+OBJSSL = $(BUILD)/prf.o $(BUILD)/prf_md5sha1.o $(BUILD)/prf_sha256.o $(BUILD)/prf_sha384.o $(BUILD)/ssl_client.o $(BUILD)/ssl_client_full.o $(BUILD)/ssl_engine.o $(BUILD)/ssl_hashes.o $(BUILD)/ssl_hs_client.o $(BUILD)/ssl_hs_server.o $(BUILD)/ssl_io.o $(BUILD)/ssl_lru.o $(BUILD)/ssl_rec_cbc.o $(BUILD)/ssl_rec_gcm.o $(BUILD)/ssl_server.o $(BUILD)/ssl_server_mine2g.o $(BUILD)/ssl_server_minf2g.o $(BUILD)/ssl_server_minr2g.o $(BUILD)/ssl_server_minu2g.o $(BUILD)/ssl_server_minv2g.o $(BUILD)/ssl_server_full_ec.o $(BUILD)/ssl_server_full_rsa.o $(BUILD)/ssl_single_ec.o $(BUILD)/ssl_single_rsa.o
+OBJSYMCIPHER = $(BUILD)/aes_big_cbcdec.o $(BUILD)/aes_big_cbcenc.o $(BUILD)/aes_big_ctr.o $(BUILD)/aes_big_dec.o $(BUILD)/aes_big_enc.o $(BUILD)/aes_common.o $(BUILD)/aes_ct.o $(BUILD)/aes_ct64.o $(BUILD)/aes_ct64_cbcdec.o $(BUILD)/aes_ct64_cbcenc.o $(BUILD)/aes_ct64_ctr.o $(BUILD)/aes_ct64_dec.o $(BUILD)/aes_ct64_enc.o $(BUILD)/aes_ct_cbcdec.o $(BUILD)/aes_ct_cbcenc.o $(BUILD)/aes_ct_ctr.o $(BUILD)/aes_ct_dec.o $(BUILD)/aes_ct_enc.o $(BUILD)/aes_small_cbcdec.o $(BUILD)/aes_small_cbcenc.o $(BUILD)/aes_small_ctr.o $(BUILD)/aes_small_dec.o $(BUILD)/aes_small_enc.o $(BUILD)/des_ct.o $(BUILD)/des_ct_cbcdec.o $(BUILD)/des_ct_cbcenc.o $(BUILD)/des_support.o $(BUILD)/des_tab.o $(BUILD)/des_tab_cbcdec.o $(BUILD)/des_tab_cbcenc.o
+OBJX509 = $(BUILD)/skey_decoder.o $(BUILD)/x509_decoder.o $(BUILD)/x509_knownkey.o $(BUILD)/x509_minimal.o
+OBJ = $(OBJCODEC) $(OBJEC) $(OBJHASH) $(OBJINT31) $(OBJINT32) $(OBJMAC) $(OBJRAND) $(OBJRSA) $(OBJSSL) $(OBJSYMCIPHER) $(OBJX509)
+OBJBRSSL = $(BUILD)/brssl.o $(BUILD)/certs.o $(BUILD)/chain.o $(BUILD)/client.o $(BUILD)/errors.o $(BUILD)/files.o $(BUILD)/keys.o $(BUILD)/names.o $(BUILD)/server.o $(BUILD)/skey.o $(BUILD)/sslio.o $(BUILD)/ta.o $(BUILD)/vector.o $(BUILD)/verify.o $(BUILD)/xmem.o
+OBJTESTCRYPTO = $(BUILD)/test_crypto.o
+OBJTESTSPEED = $(BUILD)/test_speed.o
+OBJTESTX509 = $(BUILD)/test_x509.o
+OBJTESTMATH = $(BUILD)/test_math.o
+
+T0COMP = T0Comp.exe
+T0SRC = T0/BlobWriter.cs T0/CPU.cs T0/CodeElement.cs T0/CodeElementJump.cs T0/CodeElementUInt.cs T0/CodeElementUIntExpr.cs T0/CodeElementUIntInt.cs T0/CodeElementUIntUInt.cs T0/ConstData.cs T0/Opcode.cs T0/OpcodeCall.cs T0/OpcodeConst.cs T0/OpcodeGetLocal.cs T0/OpcodeJump.cs T0/OpcodeJumpIf.cs T0/OpcodeJumpIfNot.cs T0/OpcodeJumpUncond.cs T0/OpcodePutLocal.cs T0/OpcodeRet.cs T0/SType.cs T0/T0Comp.cs T0/TPointerBase.cs T0/TPointerBlob.cs T0/TPointerExpr.cs T0/TPointerNull.cs T0/TPointerXT.cs T0/TValue.cs T0/Word.cs T0/WordBuilder.cs T0/WordData.cs T0/WordInterpreted.cs T0/WordNative.cs
+T0KERN = T0/kern.t0
+
+all: compile
+
+compile: $(BEARSSLLIB) $(BRSSL) $(TESTCRYPTO) $(TESTSPEED) $(TESTX509)
+
+$(BEARSSLLIB): $(BUILD) $(OBJ)
+       $(AR) $(BEARSSLLIB) $(OBJ)
+
+$(BRSSL): $(BEARSSLLIB) $(OBJBRSSL)
+       $(CC) $(LDFLAGS) -o $(BRSSL) $(OBJBRSSL) $(BEARSSLLIB)
+
+$(TESTCRYPTO): $(BEARSSLLIB) $(OBJTESTCRYPTO)
+       $(CC) $(LDFLAGS) -o $(TESTCRYPTO) $(OBJTESTCRYPTO) $(BEARSSLLIB)
+
+$(TESTSPEED): $(BEARSSLLIB) $(OBJTESTSPEED)
+       $(CC) $(LDFLAGS) -o $(TESTSPEED) $(OBJTESTSPEED) $(BEARSSLLIB)
+
+$(TESTX509): $(BEARSSLLIB) $(OBJTESTX509)
+       $(CC) $(LDFLAGS) -o $(TESTX509) $(OBJTESTX509) $(BEARSSLLIB)
+
+$(TESTMATH): $(BEARSSLLIB) $(OBJTESTMATH)
+       $(CC) $(LDFLAGS) -o $(TESTMATH) $(OBJTESTMATH) $(BEARSSLLIB) -lgmp
+
+$(BUILD):
+       -mkdir -p $(BUILD)
+
+T0: $(T0COMP) T0Gen
+
+T0Gen:
+       mono T0Comp.exe -o src/codec/pemdec -r br_pem_decoder src/codec/pemdec.t0
+       mono T0Comp.exe -o src/ssl/ssl_hs_client -r br_ssl_hs_client src/ssl/ssl_hs_common.t0 src/ssl/ssl_hs_client.t0
+       mono T0Comp.exe -o src/ssl/ssl_hs_server -r br_ssl_hs_server src/ssl/ssl_hs_common.t0 src/ssl/ssl_hs_server.t0
+       mono T0Comp.exe -o src/x509/skey_decoder -r br_skey_decoder src/x509/asn1.t0 src/x509/skey_decoder.t0
+       mono T0Comp.exe -o src/x509/x509_decoder -r br_x509_decoder src/x509/asn1.t0 src/x509/x509_decoder.t0
+       mono T0Comp.exe -o src/x509/x509_minimal -r br_x509_minimal src/x509/asn1.t0 src/x509/x509_minimal.t0
+
+$(T0COMP): $(T0SRC) $(T0KERN)
+       ./mkT0.sh
+
+clean:
+       -rm -f $(OBJ) $(BEARSSLLIB) $(OBJSSL) $(BRSSL) $(OBJBRSSL) $(TESTCRYPTO) $(OBJTESTCRYPTO) $(TESTSPEED) $(OBJTESTSPEED) $(TESTX509) $(OBJTESTX509) $(TESTMATH) $(OBJTESTMATH)
+
+$(BUILD)/ccopy.o: src/codec/ccopy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ccopy.o src/codec/ccopy.c
+
+$(BUILD)/dec16be.o: src/codec/dec16be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec16be.o src/codec/dec16be.c
+
+$(BUILD)/dec16le.o: src/codec/dec16le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec16le.o src/codec/dec16le.c
+
+$(BUILD)/dec32be.o: src/codec/dec32be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec32be.o src/codec/dec32be.c
+
+$(BUILD)/dec32le.o: src/codec/dec32le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec32le.o src/codec/dec32le.c
+
+$(BUILD)/dec64be.o: src/codec/dec64be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec64be.o src/codec/dec64be.c
+
+$(BUILD)/dec64le.o: src/codec/dec64le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec64le.o src/codec/dec64le.c
+
+$(BUILD)/enc16be.o: src/codec/enc16be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc16be.o src/codec/enc16be.c
+
+$(BUILD)/enc16le.o: src/codec/enc16le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc16le.o src/codec/enc16le.c
+
+$(BUILD)/enc32be.o: src/codec/enc32be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc32be.o src/codec/enc32be.c
+
+$(BUILD)/enc32le.o: src/codec/enc32le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc32le.o src/codec/enc32le.c
+
+$(BUILD)/enc64be.o: src/codec/enc64be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc64be.o src/codec/enc64be.c
+
+$(BUILD)/enc64le.o: src/codec/enc64le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc64le.o src/codec/enc64le.c
+
+$(BUILD)/pemdec.o: src/codec/pemdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/pemdec.o src/codec/pemdec.c
+
+$(BUILD)/ec_g_secp256r1.o: src/ec/ec_g_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp256r1.o src/ec/ec_g_secp256r1.c
+
+$(BUILD)/ec_g_secp384r1.o: src/ec/ec_g_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp384r1.o src/ec/ec_g_secp384r1.c
+
+$(BUILD)/ec_g_secp521r1.o: src/ec/ec_g_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp521r1.o src/ec/ec_g_secp521r1.c
+
+
+$(BUILD)/ec_prime_i31.o: src/ec/ec_prime_i31.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31.o src/ec/ec_prime_i31.c
+
+$(BUILD)/ec_prime_i31_secp256r1.o: src/ec/ec_prime_i31_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp256r1.o src/ec/ec_prime_i31_secp256r1.c
+
+$(BUILD)/ec_prime_i31_secp384r1.o: src/ec/ec_prime_i31_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp384r1.o src/ec/ec_prime_i31_secp384r1.c
+
+$(BUILD)/ec_prime_i31_secp521r1.o: src/ec/ec_prime_i31_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp521r1.o src/ec/ec_prime_i31_secp521r1.c
+
+$(BUILD)/ec_secp256r1.o: src/ec/ec_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp256r1.o src/ec/ec_secp256r1.c
+
+$(BUILD)/ec_secp384r1.o: src/ec/ec_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp384r1.o src/ec/ec_secp384r1.c
+
+$(BUILD)/ec_secp521r1.o: src/ec/ec_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp521r1.o src/ec/ec_secp521r1.c
+
+$(BUILD)/ecdsa_atr.o: src/ec/ecdsa_atr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_atr.o src/ec/ecdsa_atr.c
+
+$(BUILD)/ecdsa_i31_bits.o: src/ec/ecdsa_i31_bits.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_bits.o src/ec/ecdsa_i31_bits.c
+
+$(BUILD)/ecdsa_i31_sign_asn1.o: src/ec/ecdsa_i31_sign_asn1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_sign_asn1.o src/ec/ecdsa_i31_sign_asn1.c
+
+$(BUILD)/ecdsa_i31_sign_raw.o: src/ec/ecdsa_i31_sign_raw.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_sign_raw.o src/ec/ecdsa_i31_sign_raw.c
+
+$(BUILD)/ecdsa_i31_vrfy_asn1.o: src/ec/ecdsa_i31_vrfy_asn1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_vrfy_asn1.o src/ec/ecdsa_i31_vrfy_asn1.c
+
+$(BUILD)/ecdsa_i31_vrfy_raw.o: src/ec/ecdsa_i31_vrfy_raw.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_vrfy_raw.o src/ec/ecdsa_i31_vrfy_raw.c
+
+$(BUILD)/ecdsa_rta.o: src/ec/ecdsa_rta.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_rta.o src/ec/ecdsa_rta.c
+
+$(BUILD)/dig_oid.o: src/hash/dig_oid.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dig_oid.o src/hash/dig_oid.c
+
+$(BUILD)/dig_size.o: src/hash/dig_size.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dig_size.o src/hash/dig_size.c
+
+$(BUILD)/ghash_ctmul.o: src/hash/ghash_ctmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul.o src/hash/ghash_ctmul.c
+
+$(BUILD)/ghash_ctmul32.o: src/hash/ghash_ctmul32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul32.o src/hash/ghash_ctmul32.c
+
+$(BUILD)/ghash_ctmul64.o: src/hash/ghash_ctmul64.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul64.o src/hash/ghash_ctmul64.c
+
+$(BUILD)/md5.o: src/hash/md5.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/md5.o src/hash/md5.c
+
+$(BUILD)/md5sha1.o: src/hash/md5sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/md5sha1.o src/hash/md5sha1.c
+
+$(BUILD)/multihash.o: src/hash/multihash.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/multihash.o src/hash/multihash.c
+
+$(BUILD)/sha1.o: src/hash/sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha1.o src/hash/sha1.c
+
+$(BUILD)/sha2big.o: src/hash/sha2big.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha2big.o src/hash/sha2big.c
+
+$(BUILD)/sha2small.o: src/hash/sha2small.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha2small.o src/hash/sha2small.c
+
+$(BUILD)/i31_add.o: src/int/i31_add.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_add.o src/int/i31_add.c
+
+$(BUILD)/i31_bitlen.o: src/int/i31_bitlen.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_bitlen.o src/int/i31_bitlen.c
+
+$(BUILD)/i31_decmod.o: src/int/i31_decmod.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decmod.o src/int/i31_decmod.c
+
+$(BUILD)/i31_decode.o: src/int/i31_decode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decode.o src/int/i31_decode.c
+
+$(BUILD)/i31_decred.o: src/int/i31_decred.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decred.o src/int/i31_decred.c
+
+$(BUILD)/i31_encode.o: src/int/i31_encode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_encode.o src/int/i31_encode.c
+
+$(BUILD)/i31_fmont.o: src/int/i31_fmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_fmont.o src/int/i31_fmont.c
+
+$(BUILD)/i31_iszero.o: src/int/i31_iszero.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_iszero.o src/int/i31_iszero.c
+
+$(BUILD)/i31_modpow.o: src/int/i31_modpow.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_modpow.o src/int/i31_modpow.c
+
+$(BUILD)/i31_montmul.o: src/int/i31_montmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_montmul.o src/int/i31_montmul.c
+
+$(BUILD)/i31_mulacc.o: src/int/i31_mulacc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_mulacc.o src/int/i31_mulacc.c
+
+$(BUILD)/i31_muladd.o: src/int/i31_muladd.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_muladd.o src/int/i31_muladd.c
+
+$(BUILD)/i31_ninv31.o: src/int/i31_ninv31.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_ninv31.o src/int/i31_ninv31.c
+
+$(BUILD)/i31_reduce.o: src/int/i31_reduce.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_reduce.o src/int/i31_reduce.c
+
+$(BUILD)/i31_rshift.o: src/int/i31_rshift.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_rshift.o src/int/i31_rshift.c
+
+$(BUILD)/i31_sub.o: src/int/i31_sub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_sub.o src/int/i31_sub.c
+
+$(BUILD)/i31_tmont.o: src/int/i31_tmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_tmont.o src/int/i31_tmont.c
+
+$(BUILD)/i32_add.o: src/int/i32_add.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_add.o src/int/i32_add.c
+
+$(BUILD)/i32_bitlen.o: src/int/i32_bitlen.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_bitlen.o src/int/i32_bitlen.c
+
+$(BUILD)/i32_decmod.o: src/int/i32_decmod.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decmod.o src/int/i32_decmod.c
+
+$(BUILD)/i32_decode.o: src/int/i32_decode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decode.o src/int/i32_decode.c
+
+$(BUILD)/i32_decred.o: src/int/i32_decred.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decred.o src/int/i32_decred.c
+
+$(BUILD)/i32_div32.o: src/int/i32_div32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_div32.o src/int/i32_div32.c
+
+$(BUILD)/i32_encode.o: src/int/i32_encode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_encode.o src/int/i32_encode.c
+
+$(BUILD)/i32_fmont.o: src/int/i32_fmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_fmont.o src/int/i32_fmont.c
+
+$(BUILD)/i32_iszero.o: src/int/i32_iszero.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_iszero.o src/int/i32_iszero.c
+
+$(BUILD)/i32_modpow.o: src/int/i32_modpow.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_modpow.o src/int/i32_modpow.c
+
+$(BUILD)/i32_montmul.o: src/int/i32_montmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_montmul.o src/int/i32_montmul.c
+
+$(BUILD)/i32_mulacc.o: src/int/i32_mulacc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_mulacc.o src/int/i32_mulacc.c
+
+$(BUILD)/i32_muladd.o: src/int/i32_muladd.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_muladd.o src/int/i32_muladd.c
+
+$(BUILD)/i32_ninv32.o: src/int/i32_ninv32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_ninv32.o src/int/i32_ninv32.c
+
+$(BUILD)/i32_reduce.o: src/int/i32_reduce.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_reduce.o src/int/i32_reduce.c
+
+$(BUILD)/i32_sub.o: src/int/i32_sub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_sub.o src/int/i32_sub.c
+
+$(BUILD)/i32_tmont.o: src/int/i32_tmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_tmont.o src/int/i32_tmont.c
+
+$(BUILD)/hmac.o: src/mac/hmac.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac.o src/mac/hmac.c
+
+$(BUILD)/hmac_ct.o: src/mac/hmac_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac_ct.o src/mac/hmac_ct.c
+
+$(BUILD)/hmac_drbg.o: src/rand/hmac_drbg.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac_drbg.o src/rand/hmac_drbg.c
+
+$(BUILD)/rsa_i31_pkcs1_sign.o: src/rsa/rsa_i31_pkcs1_sign.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pkcs1_sign.o src/rsa/rsa_i31_pkcs1_sign.c
+
+$(BUILD)/rsa_i31_pkcs1_vrfy.o: src/rsa/rsa_i31_pkcs1_vrfy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pkcs1_vrfy.o src/rsa/rsa_i31_pkcs1_vrfy.c
+
+$(BUILD)/rsa_i31_priv.o: src/rsa/rsa_i31_priv.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_priv.o src/rsa/rsa_i31_priv.c
+
+$(BUILD)/rsa_i31_pub.o: src/rsa/rsa_i31_pub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pub.o src/rsa/rsa_i31_pub.c
+
+$(BUILD)/rsa_i32_pkcs1_sign.o: src/rsa/rsa_i32_pkcs1_sign.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pkcs1_sign.o src/rsa/rsa_i32_pkcs1_sign.c
+
+$(BUILD)/rsa_i32_pkcs1_vrfy.o: src/rsa/rsa_i32_pkcs1_vrfy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pkcs1_vrfy.o src/rsa/rsa_i32_pkcs1_vrfy.c
+
+$(BUILD)/rsa_i32_priv.o: src/rsa/rsa_i32_priv.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_priv.o src/rsa/rsa_i32_priv.c
+
+$(BUILD)/rsa_i32_pub.o: src/rsa/rsa_i32_pub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pub.o src/rsa/rsa_i32_pub.c
+
+$(BUILD)/rsa_ssl_decrypt.o: src/rsa/rsa_ssl_decrypt.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_ssl_decrypt.o src/rsa/rsa_ssl_decrypt.c
+
+$(BUILD)/prf.o: src/ssl/prf.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf.o src/ssl/prf.c
+
+$(BUILD)/prf_md5sha1.o: src/ssl/prf_md5sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_md5sha1.o src/ssl/prf_md5sha1.c
+
+$(BUILD)/prf_sha256.o: src/ssl/prf_sha256.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_sha256.o src/ssl/prf_sha256.c
+
+$(BUILD)/prf_sha384.o: src/ssl/prf_sha384.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_sha384.o src/ssl/prf_sha384.c
+
+$(BUILD)/ssl_client.o: src/ssl/ssl_client.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_client.o src/ssl/ssl_client.c
+
+$(BUILD)/ssl_client_full.o: src/ssl/ssl_client_full.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_client_full.o src/ssl/ssl_client_full.c
+
+$(BUILD)/ssl_engine.o: src/ssl/ssl_engine.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_engine.o src/ssl/ssl_engine.c
+
+$(BUILD)/ssl_hashes.o: src/ssl/ssl_hashes.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hashes.o src/ssl/ssl_hashes.c
+
+$(BUILD)/ssl_hs_client.o: src/ssl/ssl_hs_client.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hs_client.o src/ssl/ssl_hs_client.c
+
+$(BUILD)/ssl_hs_server.o: src/ssl/ssl_hs_server.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hs_server.o src/ssl/ssl_hs_server.c
+
+$(BUILD)/ssl_io.o: src/ssl/ssl_io.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_io.o src/ssl/ssl_io.c
+
+$(BUILD)/ssl_lru.o: src/ssl/ssl_lru.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_lru.o src/ssl/ssl_lru.c
+
+$(BUILD)/ssl_rec_cbc.o: src/ssl/ssl_rec_cbc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_rec_cbc.o src/ssl/ssl_rec_cbc.c
+
+$(BUILD)/ssl_rec_gcm.o: src/ssl/ssl_rec_gcm.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_rec_gcm.o src/ssl/ssl_rec_gcm.c
+
+$(BUILD)/ssl_server.o: src/ssl/ssl_server.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server.o src/ssl/ssl_server.c
+
+$(BUILD)/ssl_server_mine2g.o: src/ssl/ssl_server_mine2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_mine2g.o src/ssl/ssl_server_mine2g.c
+
+$(BUILD)/ssl_server_minf2g.o: src/ssl/ssl_server_minf2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minf2g.o src/ssl/ssl_server_minf2g.c
+
+$(BUILD)/ssl_server_minr2g.o: src/ssl/ssl_server_minr2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minr2g.o src/ssl/ssl_server_minr2g.c
+
+$(BUILD)/ssl_server_minu2g.o: src/ssl/ssl_server_minu2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minu2g.o src/ssl/ssl_server_minu2g.c
+
+$(BUILD)/ssl_server_minv2g.o: src/ssl/ssl_server_minv2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minv2g.o src/ssl/ssl_server_minv2g.c
+
+$(BUILD)/ssl_server_full_ec.o: src/ssl/ssl_server_full_ec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_full_ec.o src/ssl/ssl_server_full_ec.c
+
+$(BUILD)/ssl_server_full_rsa.o: src/ssl/ssl_server_full_rsa.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_full_rsa.o src/ssl/ssl_server_full_rsa.c
+
+$(BUILD)/ssl_single_ec.o: src/ssl/ssl_single_ec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_single_ec.o src/ssl/ssl_single_ec.c
+
+$(BUILD)/ssl_single_rsa.o: src/ssl/ssl_single_rsa.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_single_rsa.o src/ssl/ssl_single_rsa.c
+
+$(BUILD)/aes_big_cbcdec.o: src/symcipher/aes_big_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_cbcdec.o src/symcipher/aes_big_cbcdec.c
+
+$(BUILD)/aes_big_cbcenc.o: src/symcipher/aes_big_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_cbcenc.o src/symcipher/aes_big_cbcenc.c
+
+$(BUILD)/aes_big_ctr.o: src/symcipher/aes_big_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_ctr.o src/symcipher/aes_big_ctr.c
+
+$(BUILD)/aes_big_dec.o: src/symcipher/aes_big_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_dec.o src/symcipher/aes_big_dec.c
+
+$(BUILD)/aes_big_enc.o: src/symcipher/aes_big_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_enc.o src/symcipher/aes_big_enc.c
+
+$(BUILD)/aes_common.o: src/symcipher/aes_common.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_common.o src/symcipher/aes_common.c
+
+$(BUILD)/aes_ct.o: src/symcipher/aes_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct.o src/symcipher/aes_ct.c
+
+$(BUILD)/aes_ct64.o: src/symcipher/aes_ct64.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64.o src/symcipher/aes_ct64.c
+
+$(BUILD)/aes_ct64_cbcdec.o: src/symcipher/aes_ct64_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_cbcdec.o src/symcipher/aes_ct64_cbcdec.c
+
+$(BUILD)/aes_ct64_cbcenc.o: src/symcipher/aes_ct64_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_cbcenc.o src/symcipher/aes_ct64_cbcenc.c
+
+$(BUILD)/aes_ct64_ctr.o: src/symcipher/aes_ct64_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_ctr.o src/symcipher/aes_ct64_ctr.c
+
+$(BUILD)/aes_ct64_dec.o: src/symcipher/aes_ct64_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_dec.o src/symcipher/aes_ct64_dec.c
+
+$(BUILD)/aes_ct64_enc.o: src/symcipher/aes_ct64_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_enc.o src/symcipher/aes_ct64_enc.c
+
+$(BUILD)/aes_ct_cbcdec.o: src/symcipher/aes_ct_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_cbcdec.o src/symcipher/aes_ct_cbcdec.c
+
+$(BUILD)/aes_ct_cbcenc.o: src/symcipher/aes_ct_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_cbcenc.o src/symcipher/aes_ct_cbcenc.c
+
+$(BUILD)/aes_ct_ctr.o: src/symcipher/aes_ct_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_ctr.o src/symcipher/aes_ct_ctr.c
+
+$(BUILD)/aes_ct_dec.o: src/symcipher/aes_ct_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_dec.o src/symcipher/aes_ct_dec.c
+
+$(BUILD)/aes_ct_enc.o: src/symcipher/aes_ct_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_enc.o src/symcipher/aes_ct_enc.c
+
+$(BUILD)/aes_small_cbcdec.o: src/symcipher/aes_small_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_cbcdec.o src/symcipher/aes_small_cbcdec.c
+
+$(BUILD)/aes_small_cbcenc.o: src/symcipher/aes_small_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_cbcenc.o src/symcipher/aes_small_cbcenc.c
+
+$(BUILD)/aes_small_ctr.o: src/symcipher/aes_small_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_ctr.o src/symcipher/aes_small_ctr.c
+
+$(BUILD)/aes_small_dec.o: src/symcipher/aes_small_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_dec.o src/symcipher/aes_small_dec.c
+
+$(BUILD)/aes_small_enc.o: src/symcipher/aes_small_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_enc.o src/symcipher/aes_small_enc.c
+
+$(BUILD)/des_ct.o: src/symcipher/des_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct.o src/symcipher/des_ct.c
+
+$(BUILD)/des_ct_cbcdec.o: src/symcipher/des_ct_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct_cbcdec.o src/symcipher/des_ct_cbcdec.c
+
+$(BUILD)/des_ct_cbcenc.o: src/symcipher/des_ct_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct_cbcenc.o src/symcipher/des_ct_cbcenc.c
+
+$(BUILD)/des_support.o: src/symcipher/des_support.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_support.o src/symcipher/des_support.c
+
+$(BUILD)/des_tab.o: src/symcipher/des_tab.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab.o src/symcipher/des_tab.c
+
+$(BUILD)/des_tab_cbcdec.o: src/symcipher/des_tab_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab_cbcdec.o src/symcipher/des_tab_cbcdec.c
+
+$(BUILD)/des_tab_cbcenc.o: src/symcipher/des_tab_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab_cbcenc.o src/symcipher/des_tab_cbcenc.c
+
+$(BUILD)/skey_decoder.o: src/x509/skey_decoder.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/skey_decoder.o src/x509/skey_decoder.c
+
+$(BUILD)/x509_decoder.o: src/x509/x509_decoder.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_decoder.o src/x509/x509_decoder.c
+
+$(BUILD)/x509_knownkey.o: src/x509/x509_knownkey.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_knownkey.o src/x509/x509_knownkey.c
+
+$(BUILD)/x509_minimal.o: src/x509/x509_minimal.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_minimal.o src/x509/x509_minimal.c
+
+$(BUILD)/test_crypto.o: test/test_crypto.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_crypto.o test/test_crypto.c
+
+$(BUILD)/test_math.o: test/test_math.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_math.o test/test_math.c
+
+$(BUILD)/test_speed.o: test/test_speed.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_speed.o test/test_speed.c
+
+$(BUILD)/test_x509.o: test/test_x509.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_x509.o test/test_x509.c
+
+$(BUILD)/brssl.o: tools/brssl.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/brssl.o tools/brssl.c
+
+$(BUILD)/certs.o: tools/certs.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/certs.o tools/certs.c
+
+$(BUILD)/chain.o: tools/chain.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/chain.o tools/chain.c
+
+$(BUILD)/client.o: tools/client.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/client.o tools/client.c
+
+$(BUILD)/errors.o: tools/errors.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/errors.o tools/errors.c
+
+$(BUILD)/files.o: tools/files.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/files.o tools/files.c
+
+$(BUILD)/keys.o: tools/keys.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/keys.o tools/keys.c
+
+$(BUILD)/names.o: tools/names.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/names.o tools/names.c
+
+$(BUILD)/server.o: tools/server.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/server.o tools/server.c
+
+$(BUILD)/skey.o: tools/skey.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/skey.o tools/skey.c
+
+$(BUILD)/sslio.o: tools/sslio.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sslio.o tools/sslio.c
+
+$(BUILD)/ta.o: tools/ta.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ta.o tools/ta.c
+
+$(BUILD)/vector.o: tools/vector.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/vector.o tools/vector.c
+
+$(BUILD)/verify.o: tools/verify.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/verify.o tools/verify.c
+
+$(BUILD)/xmem.o: tools/xmem.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/xmem.o tools/xmem.c
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..e72537d
--- /dev/null
@@ -0,0 +1,169 @@
+# Disclaimer
+
+BearSSL is for now considered alpha-level software. This means that it
+probably still has some bugs, possibly very serious ones (e.g. buffer
+overflows -- one of the perks of using C as programming language). It
+still lacks some functionalities. The API will probably change and may
+break both source and binary compatibility.
+
+In other words, you would be quite mad to use it for any production
+purpose. Right now, this is for learning, testing and possibly
+contributing.
+
+The usage license is explicited in the `LICENSE.txt` file. This is the
+"MIT license". It can be summarised in the following way:
+
+ - You can use and reuse the library as you wish, and modify it, and
+   integrate it in your own code, and distribute it as is or in any
+   modified form, and so on.
+
+ - The only obligation that the license terms put upon you is that you
+   acknowledge and make it clear that if anything breaks, it is not my
+   fault, and I am not liable for anything, regardless of the type and
+   amount of collateral damage. The license terms say that the copyright
+   notice "shall be included in all copies or substantial portions of
+   the Software": this is how the disclaimer is "made explicit".
+   Basically, I have put it in every source file, so just keep it there.
+
+# Installation
+
+As of version 0.1, BearSSL is a simple static library. Most of the
+process is rather manual and old-style, and there is no installer (this
+will be added in a later version, in particular when all the man pages
+for BearSSL functions are written).
+
+ 1. Have a look at the top of the `Makefile`. There you can configure the
+    command names and flags for invoking the C compiler, linker, and
+    static library archiver.
+
+ 2. There are a few configurable switches in `src/config.h`. These switches
+    relate to compile-time options, e.g. support of a system-provided
+    random source. On usual platforms (e.g. Linux or OS X), auto-detection
+    should work, but you can always override things with `config.h`.
+
+ 3. Type `make`. This should produce the static library (`libbearssl.a`),
+    the test executables (`testcrypto`, `testspeed` and `testx509`), and
+    the command-line debug tool (`brssl`). You might want to run the tests:
+
+     - `testcrypto all` runs the cryptographic tests (test vectors on all
+       implemented cryptogaphic functions). It can be slow.
+
+     - `testspeed all` runs a number of performance benchmarks, there again
+       on cryptographic functions. It gives a taste of how things go on the
+       current platform.
+
+     - `testx509` runs X.509 validation tests. The test certificates are
+       all in `test/x509/`.
+
+ 4. The `brssl` command-line tool is a stand-alone binary. It can exercise
+    some of the functionalities of BearSSL, in particular running a test
+    SSL client or server. It is not meant for production purposes (e.g.
+    the SSL client has a mode where it disregards the inability to validate
+    the server's certificate, which is inherently unsafe, but convenient
+    for debug).
+
+ 5. Using the library means writing some application code that invokes it,
+    and linking with the static library. The header files are all in the
+    `inc` directory; copy them wherever makes sense (e.g. in the
+    `/usr/local/include` directory). The library itself (`libbearssl.a`)
+    is what you link against.
+
+    Alternatively, you may want to copy the source files directly into
+    your own application code. This will make integrating ulterior versions
+    of BearSSL more difficult. If you still want to go down that road,
+    then simply copy all the `*.h` and `*.c` files from the `src` and `inc`
+    directories into your application source code. In the BearSSL source
+    archive, the source files are segregated into various sub-directories,
+    but this is for my convenience only. There is no technical requirement
+    for that, and all files can be dumped together in a simple directory.
+
+    Dependencies are simple and systematic:
+
+     - Each `*.c` file includes `inner.h`
+     - `inner.h` includes `config.h` and `bearssl.h`
+     - `bearssl.h` includes the other `bearssl_*.h`
+
+# Versioning
+
+I follow this simple version numbering scheme:
+
+ - Version numbers are `x.y` or `x.y.z` where `x`, `y` ans `z` are
+   decimal integers (possibly greater than 10). When the `.z` part is
+   missing, it is equivalent to `.0`.
+
+ - Backward compatibility is maintained, at both source and binary levels,
+   for each major version: this means that if some application code was
+   designed for version `x.y`, then it should compile, link and run
+   properly with any version `x.y'` for any `y'` greater than `y`.
+
+   The major version `0` is an exception. You shall not expect that any
+   version that starts with `0.` offers any kind of compatibility,
+   either source or binary, with any other `0.` version. (Of course I
+   will try to maintain some decent level of source compatibility, but I
+   make no promise in that respect. Since the API uses caller-allocated
+   context structures, I already know that binary compatibility _will_
+   be broken.)
+
+ - Sub-versions (the `y` part) are about added functionality. That is,
+   it can be expected that `1.3` will contain some extra functions when
+   compared to `1.2`. The next version level (the `z` part) is for
+   bugfixes that do not add any functionality.
+
+# API Usage
+
+Right now there is little documentation. The following principles are
+maintained:
+
+ - All public symbols (global functions and data elements, macros) have
+   a name that starts with `br_` or `BR_`.
+
+ - The header files (the `bearssl_*.h` in the `inc` directory) contain
+   for now the most complete documentation (as comments).
+
+ - Context structures are allocated by the caller. BearSSL does not
+   contain any single `malloc()` call; this means that there is no
+   "freeing up" call to be done. When you don't need some BearSSL
+   functionality, just cease to call it, and that's it.
+
+ - BearSSL contains no modifiable static data. It is thus thread-safe
+   and reentrant, _for distinct contexts_. Accessing the same context
+   structure from distinct threads, though, is a recipe for disaster.
+
+ - The main SSL I/O API is organised as a state machine. A running
+   SSL engine (client or server) has four I/O ports:
+
+    - It can receive bytes from the transport medium ("record data").
+    - It can send bytes to the transport medium.
+    - It can receive application data, to be sent to the peer through
+      the SSL tunnel.
+    - It can produce application data, built from the records sent by
+      the peer.
+
+   BearSSL never performs I/O by itself; it expects the caller to
+   provide or retrieve the data. Each port consists in a pair of
+   functions: one yields the pointer to the buffer from which data
+   can be read or to which data can be written, and the maximum
+   size for such an operation; the other function is used to
+   inform the engine about how many bytes were actually read or
+   written.
+
+   For instance, if the `br_ssl_engine_sendrec_buf()` function returns a
+   non-NULL pointer, then this means that there are bytes to be sent to
+   the transport medium. When the caller has indeed sent some or all of
+   these bytes, it informs the engine with
+   `br_ssl_engine_sendrec_ack()`.
+
+   This state-machine API means that the engine never blocks. Each
+   invocation may trigger computations, but will always return as
+   promptly as the CPU power allows. All the I/O waiting is supposed to
+   be done on the outside. This structure allows managing several
+   concurrent SSL engines, along with other I/O tasks, with a single
+   mono-threaded loop using `select()` or `poll()`. It also makes it
+   easier to integrate BearSSL with various transport mechanisms (e.g.
+   messages in the EAP-TLS authentication framework).
+
+ - Nevertheless, there are situations where simple blocking calls _can_
+   be used, and are convenient. For these situations, use the
+   `br_sslio_context` wrapper. Then do blocking reads and writes with
+   `br_sslio_read()` and similar functions. The sample client code
+   in `samples/client_basic.c` shows how such things are done.
diff --git a/T0/BlobWriter.cs b/T0/BlobWriter.cs
new file mode 100644 (file)
index 0000000..3bbc8e6
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.IO;
+using System.Text;
+
+/*
+ * A simple class for writing out bytes as hexadecimal constants or
+ * explicit expressions for the initializer of a C array of 'unsigned
+ * char'. It starts every line with a given number of tabs, and aims at
+ * keeping lines below a given threshold (each indentation tab counts as
+ * 8 characters). An explicit newline is inserted before the first
+ * element, and commas are used as separators.
+ */
+
+class BlobWriter {
+
+       TextWriter w;
+       int maxLineLen;
+       int indent;
+       int lineLen;
+
+       /*
+        * Create a new instance. 'maxLineLen' is in characters, and
+        * 'indent' is the number of tab characters at the start of
+        * each line.
+        */
+       internal BlobWriter(TextWriter w, int maxLineLen, int indent)
+       {
+               this.w = w;
+               this.maxLineLen = maxLineLen;
+               this.indent = indent;
+               lineLen = -1;
+       }
+
+       void DoNL()
+       {
+               w.WriteLine();
+               for (int i = 0; i < indent; i ++) {
+                       w.Write('\t');
+               }
+               lineLen = (indent << 3);
+       }
+
+       /*
+        * Append a new byte value; it will be converted to an hexadecimal
+        * constant in the output.
+        */
+       internal void Append(byte b)
+       {
+               if (lineLen < 0) {
+                       DoNL();
+               } else {
+                       w.Write(',');
+                       lineLen ++;
+                       if ((lineLen + 5) > maxLineLen) {
+                               DoNL();
+                       } else {
+                               w.Write(' ');
+                               lineLen ++;
+                       }
+               }
+               w.Write("0x{0:X2}", b);
+               lineLen += 4;
+       }
+
+       /*
+        * Append a C expression, which will be used as is. The expression
+        * may resolve to several bytes if it uses internal commas. The
+        * writer will try to honour the expected line length, but it
+        * won't insert a newline character inside the expression.
+        */
+       internal void Append(string expr)
+       {
+               if (lineLen < 0) {
+                       DoNL();
+               } else {
+                       w.Write(',');
+                       lineLen ++;
+                       if ((lineLen + 1 + expr.Length) > maxLineLen) {
+                               DoNL();
+                       } else {
+                               w.Write(' ');
+                               lineLen ++;
+                       }
+               }
+               w.Write("{0}", expr);
+               lineLen += expr.Length;
+       }
+}
diff --git a/T0/CPU.cs b/T0/CPU.cs
new file mode 100644 (file)
index 0000000..22f1a17
--- /dev/null
+++ b/T0/CPU.cs
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * Execution of code during compilation is done in a virtual CPU
+ * incarnated by this class, that contains the relevant registers.
+ *
+ * Accesses to the data on the stack are mapped to accesses to an
+ * internal array, with no explicit control on boundaries. Since the
+ * internal array may be larger than the actual stack contents,
+ * nonsensical accesses may still "work" to some extent. The whole
+ * thing won't derail beyond the CLR VM, though.
+ */
+
+class CPU {
+
+       /*
+        * Next instruction to execute is in ipBuf[ipOff].
+        */
+       internal Opcode[] ipBuf;
+       internal int ipOff;
+
+       /*
+        * stackBuf and stackPtr implement the data stack. The system
+        * stack uses frames; 'rsp' points to the current top frame.
+        */
+       TValue[] stackBuf;
+       int stackPtr;
+       Frame rsp;
+
+       internal CPU()
+       {
+               stackBuf = new TValue[16];
+               stackPtr = -1;
+               rsp = null;
+       }
+
+       /*
+        * Enter a function, reserving space for 'numLocals' local variables.
+        */
+       internal void Enter(Opcode[] code, int numLocals)
+       {
+               Frame f = new Frame(rsp, numLocals);
+               rsp = f;
+               f.savedIpBuf = ipBuf;
+               f.savedIpOff = ipOff;
+               ipBuf = code;
+               ipOff = 0;
+       }
+
+       /*
+        * Exit the current function.
+        */
+       internal void Exit()
+       {
+               ipBuf = rsp.savedIpBuf;
+               ipOff = rsp.savedIpOff;
+               rsp = rsp.upper;
+       }
+
+       /*
+        * Get the current stack depth (number of elements).
+        */
+       internal int Depth {
+               get {
+                       return stackPtr + 1;
+               }
+       }
+
+       /*
+        * Pop a value from the stack.
+        */
+       internal TValue Pop()
+       {
+               return stackBuf[stackPtr --];
+       }
+
+       /*
+        * Push a value on the stack.
+        */
+       internal void Push(TValue v)
+       {
+               int len = stackBuf.Length;
+               if (++ stackPtr == len) {
+                       TValue[] nbuf = new TValue[len << 1];
+                       Array.Copy(stackBuf, 0, nbuf, 0, len);
+                       stackBuf = nbuf;
+               }
+               stackBuf[stackPtr] = v;
+       }
+
+       /*
+        * Look at the value at depth 'depth' (0 is top of stack). The
+        * stack is unchanged.
+        */
+       internal TValue Peek(int depth)
+       {
+               return stackBuf[stackPtr - depth];
+       }
+
+       /*
+        * Rotate the stack at depth 'depth': the value at that depth
+        * is moved to the top of stack.
+        */
+       internal void Rot(int depth)
+       {
+               TValue v = stackBuf[stackPtr - depth];
+               Array.Copy(stackBuf, stackPtr - (depth - 1),
+                       stackBuf, stackPtr - depth, depth);
+               stackBuf[stackPtr] = v;
+       }
+
+       /*
+        * Inverse-rotate the stack at depth 'depth': the value at the
+        * top of stack is moved to that depth.
+        */
+       internal void NRot(int depth)
+       {
+               TValue v = stackBuf[stackPtr];
+               Array.Copy(stackBuf, stackPtr - depth,
+                       stackBuf, stackPtr - (depth - 1), depth);
+               stackBuf[stackPtr - depth] = v;
+       }
+
+       /*
+        * Get the current contents of the local variable 'num'.
+        */
+       internal TValue GetLocal(int num)
+       {
+               return rsp.locals[num];
+       }
+
+       /*
+        * Set the contents of the local variable 'num'.
+        */
+       internal void PutLocal(int num, TValue v)
+       {
+               rsp.locals[num] = v;
+       }
+
+       /*
+        * The system stack really is a linked list of Frame instances.
+        */
+       class Frame {
+
+               internal Frame upper;
+               internal Opcode[] savedIpBuf;
+               internal int savedIpOff;
+               internal TValue[] locals;
+
+               internal Frame(Frame upper, int numLocals)
+               {
+                       this.upper = upper;
+                       locals = new TValue[numLocals];
+               }
+       }
+}
diff --git a/T0/CodeElement.cs b/T0/CodeElement.cs
new file mode 100644 (file)
index 0000000..5731c5a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+abstract class CodeElement {
+
+       internal int Address { get; set; }
+
+       internal int LastLength { get; set; }
+
+       internal abstract int Length { get; }
+
+       internal CodeElement()
+       {
+               Address = -1;
+       }
+
+       internal virtual void SetJumpTarget(CodeElement target)
+       {
+               throw new Exception("Code element accepts no target");
+       }
+
+       internal abstract int Encode(BlobWriter bw);
+
+       internal static int Encode7EUnsigned(uint val, BlobWriter bw)
+       {
+               int len = 1;
+               for (uint w = val; w >= 0x80; w >>= 7) {
+                       len ++;
+               }
+               if (bw != null) {
+                       for (int k = (len - 1) * 7; k >= 0; k -= 7) {
+                               int x = (int)(val >> k) & 0x7F;
+                               if (k > 0) {
+                                       x |= 0x80;
+                               }
+                               bw.Append((byte)x);
+                       }
+               }
+               return len;
+       }
+
+       internal static int Encode7ESigned(int val, BlobWriter bw)
+       {
+               int len = 1;
+               if (val < 0) {
+                       for (int w = val; w < -0x40; w >>= 7) {
+                               len ++;
+                       }
+               } else {
+                       for (int w = val; w >= 0x40; w >>= 7) {
+                               len ++;
+                       }
+               }
+               if (bw != null) {
+                       for (int k = (len - 1) * 7; k >= 0; k -= 7) {
+                               int x = (int)(val >> k) & 0x7F;
+                               if (k > 0) {
+                                       x |= 0x80;
+                               }
+                               bw.Append((byte)x);
+                       }
+               }
+               return len;
+       }
+}
diff --git a/T0/CodeElementJump.cs b/T0/CodeElementJump.cs
new file mode 100644 (file)
index 0000000..5abd7eb
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementJump : CodeElement {
+
+       uint jumpType;
+       CodeElement target;
+
+       internal CodeElementJump(uint jumpType)
+       {
+               this.jumpType = jumpType;
+       }
+
+       internal override int Length {
+               get {
+                       int len = Encode7EUnsigned(jumpType, null);
+                       int joff = JumpOff;
+                       if (joff == Int32.MinValue) {
+                               len ++;
+                       } else {
+                               len += Encode7ESigned(joff, null);
+                       }
+                       return len;
+               }
+       }
+
+       internal override void SetJumpTarget(CodeElement target)
+       {
+               this.target = target;
+       }
+
+       int JumpOff {
+               get {
+                       if (target == null || Address < 0 || target.Address < 0)
+                       {
+                               return Int32.MinValue;
+                       } else {
+                               return target.Address - (Address + LastLength);
+                       }
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               if (bw == null) {
+                       return Length;
+               }
+               int len = Encode7EUnsigned(jumpType, bw);
+               int joff = JumpOff;
+               if (joff == Int32.MinValue) {
+                       throw new Exception("Unresolved addresses");
+               }
+               return len + Encode7ESigned(joff, bw);
+       }
+}
diff --git a/T0/CodeElementUInt.cs b/T0/CodeElementUInt.cs
new file mode 100644 (file)
index 0000000..e5f3607
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUInt : CodeElement {
+
+       uint val;
+
+       internal CodeElementUInt(uint val) : base()
+       {
+               this.val = val;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               return Encode7EUnsigned(val, bw);
+       }
+}
diff --git a/T0/CodeElementUIntExpr.cs b/T0/CodeElementUIntExpr.cs
new file mode 100644 (file)
index 0000000..d24ba58
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntExpr : CodeElement {
+
+       uint val;
+       TPointerExpr cx;
+       int off;
+
+       internal CodeElementUIntExpr(uint val,
+               TPointerExpr cx, int off) : base()
+       {
+               this.val = val;
+               this.cx = cx;
+               this.off = off;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val, null)
+                               + (cx.GetMaxBitLength(off) + 6) / 7;
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len1 = Encode7EUnsigned(val, bw);
+               int len2 = (cx.GetMaxBitLength(off) + 6) / 7;
+               bw.Append(String.Format("T0_INT{0}({1})",
+                       len2, cx.ToCExpr(off)));
+               return len1 + len2;
+       }
+}
diff --git a/T0/CodeElementUIntInt.cs b/T0/CodeElementUIntInt.cs
new file mode 100644 (file)
index 0000000..022ffb8
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntInt : CodeElement {
+
+       uint val1;
+       int val2;
+
+       internal CodeElementUIntInt(uint val1, int val2) : base()
+       {
+               this.val1 = val1;
+               this.val2 = val2;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val1, null)
+                               + Encode7ESigned(val2, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len = Encode7EUnsigned(val1, bw);
+               len += Encode7ESigned(val2, bw);
+               return len;
+       }
+}
diff --git a/T0/CodeElementUIntUInt.cs b/T0/CodeElementUIntUInt.cs
new file mode 100644 (file)
index 0000000..3d4ee33
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntUInt : CodeElement {
+
+       uint val1, val2;
+
+       internal CodeElementUIntUInt(uint val1, uint val2) : base()
+       {
+               this.val1 = val1;
+               this.val2 = val2;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val1, null)
+                               + Encode7EUnsigned(val2, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len = Encode7EUnsigned(val1, bw);
+               len += Encode7EUnsigned(val2, bw);
+               return len;
+       }
+}
diff --git a/T0/ConstData.cs b/T0/ConstData.cs
new file mode 100644 (file)
index 0000000..6a06b64
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+class ConstData {
+
+       internal long ID { get; private set; }
+       internal int Address { get; set; }
+       internal int Length {
+               get {
+                       return len;
+               }
+       }
+
+       byte[] buf;
+       int len;
+
+       internal ConstData(T0Comp ctx)
+       {
+               ID = ctx.NextBlobID();
+               buf = new byte[4];
+               len = 0;
+       }
+
+       void Expand(int elen)
+       {
+               int tlen = len + elen;
+               if (tlen > buf.Length) {
+                       int nlen = Math.Max(buf.Length << 1, tlen);
+                       byte[] nbuf = new byte[nlen];
+                       Array.Copy(buf, 0, nbuf, 0, len);
+                       buf = nbuf;
+               }
+       }
+
+       internal void Add8(byte b)
+       {
+               Expand(1);
+               buf[len ++] = b;
+       }
+
+       internal void Add16(int x)
+       {
+               Expand(2);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void Add24(int x)
+       {
+               Expand(3);
+               buf[len ++] = (byte)(x >> 16);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void Add32(int x)
+       {
+               Expand(4);
+               buf[len ++] = (byte)(x >> 24);
+               buf[len ++] = (byte)(x >> 16);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void AddString(string s)
+       {
+               byte[] sd = Encoding.UTF8.GetBytes(s);
+               Expand(sd.Length + 1);
+               Array.Copy(sd, 0, buf, len, sd.Length);
+               buf[len + sd.Length] = 0;
+               len += sd.Length + 1;
+       }
+
+       void CheckIndex(int off, int dlen)
+       {
+               if (off < 0 || off > (len - dlen)) {
+                       throw new IndexOutOfRangeException();
+               }
+       }
+
+       internal void Set8(int off, byte v)
+       {
+               CheckIndex(off, 1);
+               buf[off] = v;
+       }
+
+       internal byte Read8(int off)
+       {
+               CheckIndex(off, 1);
+               return buf[off];
+       }
+
+       internal int Read16(int off)
+       {
+               CheckIndex(off, 2);
+               return (buf[off] << 8) | buf[off + 1];
+       }
+
+       internal int Read24(int off)
+       {
+               CheckIndex(off, 3);
+               return (buf[off] << 16) | (buf[off + 1] << 8) | buf[off + 2];
+       }
+
+       internal int Read32(int off)
+       {
+               CheckIndex(off, 4);
+               return (buf[off] << 24) | (buf[off + 1] << 16)
+                       | (buf[off + 2] << 8) | buf[off + 3];
+       }
+
+       internal string ToString(int off)
+       {
+               StringBuilder sb = new StringBuilder();
+               for (;;) {
+                       int x = DecodeUTF8(ref off);
+                       if (x == 0) {
+                               return sb.ToString();
+                       }
+                       if (x < 0x10000) {
+                               sb.Append((char)x);
+                       } else {
+                               x -= 0x10000;
+                               sb.Append((char)(0xD800 + (x >> 10)));
+                               sb.Append((char)(0xDC00 + (x & 0x3FF)));
+                       }
+               }
+       }
+
+       int DecodeUTF8(ref int off)
+       {
+               if (off >= len) {
+                       throw new IndexOutOfRangeException();
+               }
+               int x = buf[off ++];
+               if (x < 0xC0 || x > 0xF7) {
+                       return x;
+               }
+               int elen, acc;
+               if (x >= 0xF0) {
+                       elen = 3;
+                       acc = x & 0x07;
+               } else if (x >= 0xE0) {
+                       elen = 2;
+                       acc = x & 0x0F;
+               } else {
+                       elen = 1;
+                       acc = x & 0x1F;
+               }
+               if (off + elen > len) {
+                       return x;
+               }
+               for (int i = 0; i < elen; i ++) {
+                       int y = buf[off + i];
+                       if (y < 0x80 || y >= 0xC0) {
+                               return x;
+                       }
+                       acc = (acc << 6) + (y & 0x3F);
+               }
+               if (acc > 0x10FFFF) {
+                       return x;
+               }
+               off += elen;
+               return acc;
+       }
+
+       internal void Encode(BlobWriter bw)
+       {
+               for (int i = 0; i < len; i ++) {
+                       bw.Append(buf[i]);
+               }
+       }
+}
diff --git a/T0/Opcode.cs b/T0/Opcode.cs
new file mode 100644 (file)
index 0000000..81d1e9d
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+abstract class Opcode {
+
+       internal Opcode()
+       {
+       }
+
+       /*
+        * Execute this opcode.
+        */
+       internal abstract void Run(CPU cpu);
+
+       /*
+        * Resolve the target (word reference) for this opcode.
+        */
+       internal virtual void ResolveTarget(Word target)
+       {
+               throw new Exception("Not a call opcode");
+       }
+
+       /*
+        * Resolve the jump offset for this opcode. Displacement is
+        * relative to the address of the opcode that immediately follows
+        * the jump code; thus, 0 implies no jump at all.
+        */
+       internal virtual void ResolveJump(int disp)
+       {
+               throw new Exception("Not a jump opcode");
+       }
+
+       /*
+        * Get the Word that this opcode references; this can happen
+        * only with "call" and "const" opcodes. For all other opcodes,
+        * this method returns null.
+        */
+       internal virtual Word GetReference(T0Comp ctx)
+       {
+               return null;
+       }
+
+       /*
+        * Get the data block that this opcode references; this can happen
+        * only with "const" opcodes. For all other opcodes, this method
+        * returns null.
+        */
+       internal virtual ConstData GetDataBlock(T0Comp ctx)
+       {
+               return null;
+       }
+
+       /*
+        * Test whether this opcode may "fall through", i.e. execution
+        * may at least potentially proceed to the next opcode.
+        */
+       internal virtual bool MayFallThrough {
+               get {
+                       return true;
+               }
+       }
+
+       /*
+        * Get jump displacement. For non-jump opcodes, this returns 0.
+        */
+       internal virtual int JumpDisp {
+               get {
+                       return 0;
+               }
+       }
+
+       /*
+        * Get stack effect for this opcode (number of elements added to
+        * the stack, could be negative). For OpcodeCall, this returns
+        * 0.
+        */
+       internal virtual int StackAction {
+               get {
+                       return 0;
+               }
+       }
+
+       internal abstract CodeElement ToCodeElement();
+
+       /*
+        * This method is called for the CodeElement corresponding to
+        * this opcode, at gcode[off]; it is used to compute actual
+        * byte jump offsets when converting code to C.
+        */
+       internal virtual void FixUp(CodeElement[] gcode, int off)
+       {
+       }
+}
diff --git a/T0/OpcodeCall.cs b/T0/OpcodeCall.cs
new file mode 100644 (file)
index 0000000..0980042
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeCall : Opcode {
+
+       Word target;
+
+       internal OpcodeCall() : this(null)
+       {
+       }
+
+       internal OpcodeCall(Word target)
+       {
+               this.target = target;
+       }
+
+       internal override void ResolveTarget(Word target)
+       {
+               if (this.target != null) {
+                       throw new Exception("Opcode already resolved");
+               }
+               this.target = target;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               target.Run(cpu);
+       }
+
+       internal override Word GetReference(T0Comp ctx)
+       {
+               if (target == null) {
+                       throw new Exception("Unresolved call target");
+               }
+               return target;
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUInt((uint)target.Slot);
+       }
+
+       public override string ToString()
+       {
+               return "call " + (target == null ? "UNRESOLVED" : target.Name);
+       }
+}
diff --git a/T0/OpcodeConst.cs b/T0/OpcodeConst.cs
new file mode 100644 (file)
index 0000000..ae75ae5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeConst : Opcode {
+
+       TValue val;
+
+       internal OpcodeConst(TValue val)
+       {
+               this.val = val;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Push(val);
+       }
+
+       internal override Word GetReference(T0Comp ctx)
+       {
+               TPointerXT xt = val.ptr as TPointerXT;
+               if (xt == null) {
+                       return null;
+               }
+               xt.Resolve(ctx);
+               return xt.Target;
+       }
+
+       internal override ConstData GetDataBlock(T0Comp ctx)
+       {
+               TPointerBlob bp = val.ptr as TPointerBlob;
+               return bp == null ? null : bp.Blob;
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               if (val.ptr == null) {
+                       return new CodeElementUIntInt(1, val.Int);
+               }
+               TPointerXT xt = val.ptr as TPointerXT;
+               if (xt != null) {
+                       if (val.x != 0) {
+                               throw new Exception(
+                                       "Cannot compile XT: non-zero offset");
+                       }
+                       return new CodeElementUIntInt(1, xt.Target.Slot);
+               }
+               TPointerBlob bp = val.ptr as TPointerBlob;
+               if (bp != null) {
+                       return new CodeElementUIntInt(1,
+                               val.x + bp.Blob.Address);
+               }
+               TPointerExpr cx = val.ptr as TPointerExpr;
+               if (cx != null) {
+                       return new CodeElementUIntExpr(1, cx, val.x);
+               }
+               throw new Exception(String.Format(
+                       "Cannot embed constant (type = {0})",
+                       val.ptr.GetType().FullName));
+       }
+
+       internal override int StackAction {
+               get {
+                       return 1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "const " + val.ToString();
+       }
+}
diff --git a/T0/OpcodeGetLocal.cs b/T0/OpcodeGetLocal.cs
new file mode 100644 (file)
index 0000000..59d24fc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeGetLocal : Opcode {
+
+       int num;
+
+       internal OpcodeGetLocal(int num)
+       {
+               this.num = num;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Push(cpu.GetLocal(num));
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUIntUInt(2, (uint)num);
+       }
+
+       internal override int StackAction {
+               get {
+                       return 1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "getlocal " + num;
+       }
+}
diff --git a/T0/OpcodeJump.cs b/T0/OpcodeJump.cs
new file mode 100644 (file)
index 0000000..4f3ec68
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+abstract class OpcodeJump : Opcode {
+
+       int disp;
+
+       internal OpcodeJump() : this(Int32.MinValue)
+       {
+       }
+
+       internal OpcodeJump(int disp)
+       {
+               this.disp = disp;
+       }
+
+       internal override int JumpDisp {
+               get {
+                       return disp;
+               }
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.ipOff += disp;
+       }
+
+       internal override void ResolveJump(int disp)
+       {
+               if (this.disp != Int32.MinValue) {
+                       throw new Exception("Jump already resolved");
+               }
+               this.disp = disp;
+       }
+
+       internal override void FixUp(CodeElement[] gcode, int off)
+       {
+               gcode[off].SetJumpTarget(gcode[off + 1 + disp]);
+       }
+}
diff --git a/T0/OpcodeJumpIf.cs b/T0/OpcodeJumpIf.cs
new file mode 100644 (file)
index 0000000..d702434
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpIf : OpcodeJump {
+
+       internal OpcodeJumpIf() : base()
+       {
+       }
+
+       internal OpcodeJumpIf(int disp) : base(disp)
+       {
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               TValue v = cpu.Pop();
+               if (v.Bool) {
+                       base.Run(cpu);
+               }
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(5);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jumpif UNRESOLVED";
+               } else {
+                       return "jumpif disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodeJumpIfNot.cs b/T0/OpcodeJumpIfNot.cs
new file mode 100644 (file)
index 0000000..afbf19d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpIfNot : OpcodeJump {
+
+       internal OpcodeJumpIfNot() : base()
+       {
+       }
+
+       internal OpcodeJumpIfNot(int disp) : base(disp)
+       {
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               TValue v = cpu.Pop();
+               if (!v.Bool) {
+                       base.Run(cpu);
+               }
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(6);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jumpifnot UNRESOLVED";
+               } else {
+                       return "jumpifnot disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodeJumpUncond.cs b/T0/OpcodeJumpUncond.cs
new file mode 100644 (file)
index 0000000..e7d8a82
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpUncond : OpcodeJump {
+
+       internal OpcodeJumpUncond() : base()
+       {
+       }
+
+       internal OpcodeJumpUncond(int disp) : base(disp)
+       {
+       }
+
+       /*
+        * Unconditional jumps do not "fall through" unless they
+        * happen to be a jump to the next instruction...
+        */
+       internal override bool MayFallThrough {
+               get {
+                       return JumpDisp == 0;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(4);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jump UNRESOLVED";
+               } else {
+                       return "jump disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodePutLocal.cs b/T0/OpcodePutLocal.cs
new file mode 100644 (file)
index 0000000..b148a65
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodePutLocal : Opcode {
+
+       int num;
+
+       internal OpcodePutLocal(int num)
+       {
+               this.num = num;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.PutLocal(num, cpu.Pop());
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUIntUInt(3, (uint)num);
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "putlocal " + num;
+       }
+}
diff --git a/T0/OpcodeRet.cs b/T0/OpcodeRet.cs
new file mode 100644 (file)
index 0000000..187473b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeRet : Opcode {
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Exit();
+       }
+
+       internal override bool MayFallThrough {
+               get {
+                       return false;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUInt(0);
+       }
+
+       public override string ToString()
+       {
+               return "ret";
+       }
+}
diff --git a/T0/SType.cs b/T0/SType.cs
new file mode 100644 (file)
index 0000000..80e9f01
--- /dev/null
@@ -0,0 +1,129 @@
+using System;
+
+/*
+ * This structure contains the stack effect of a word: number of stack
+ * element consumed on input, and number of stack element produced on
+ * output.
+ */
+
+struct SType {
+
+       /*
+        * Get number of stack elements consumed on input; this is -1 if
+        * the stack effect is not known.
+        */
+       internal int DataIn {
+               get {
+                       return din;
+               }
+       }
+
+       /*
+        * Get number of stack elements produced on output; this is -1 if
+        * either the stack effect is not known, or if the word never
+        * exits.
+        */
+       internal int DataOut {
+               get {
+                       return dout;
+               }
+       }
+
+       /*
+        * Tell whether the stack effect is known.
+        */
+       internal bool IsKnown {
+               get {
+                       return din >= 0;
+               }
+       }
+
+       /*
+        * Tell whether the stack effect is known and the word never exits.
+        */
+       internal bool NoExit {
+               get {
+                       return din >= 0 && dout < 0;
+               }
+       }
+
+       int din, dout;
+
+       internal SType(int din, int dout)
+       {
+               if (din < 0) {
+                       din = -1;
+               }
+               if (dout < 0) {
+                       dout = -1;
+               }
+               this.din = din;
+               this.dout = dout;
+       }
+
+       /*
+        * Special value for the unknown stack effect.
+        */
+       internal static SType UNKNOWN = new SType(-1, -1);
+
+       /*
+        * Constant for the "blank stack effect".
+        */
+       internal static SType BLANK = new SType(0, 0);
+
+       public static bool operator ==(SType s1, SType s2)
+       {
+               return s1.din == s2.din && s1.dout == s2.dout;
+       }
+
+       public static bool operator !=(SType s1, SType s2)
+       {
+               return s1.din != s2.din || s1.dout != s2.dout;
+       }
+
+       public override bool Equals(Object obj)
+       {
+               return (obj is SType) && ((SType)obj == this);
+       }
+
+       public override int GetHashCode()
+       {
+               return din * 31 + dout * 17;
+       }
+
+       public override string ToString()
+       {
+               if (!IsKnown) {
+                       return "UNKNOWN";
+               } else if (NoExit) {
+                       return string.Format("in:{0},noexit", din);
+               } else {
+                       return string.Format("in:{0},out:{1}", din, dout);
+               }
+       }
+
+       /*
+        * Test whether this stack effect is a sub-effect of the provided
+        * stack effect s. Stack effect s1 is a sub-effect of stack-effect
+        * s2 if any of the following holds:
+        * -- s1 and s2 are known, s1.din <= s2.din and s1 does not exit.
+        * -- s1 and s2 are known, s1.din <= s2.din, s1 and s2 exit,
+        *    and s1.din - s1.dout == s2.din - s2.dout.
+        */
+       internal bool IsSubOf(SType s)
+       {
+               if (!IsKnown || !s.IsKnown) {
+                       return false;
+               }
+               if (din > s.din) {
+                       return false;
+               }
+               if (NoExit) {
+                       return true;
+               }
+               if (s.NoExit) {
+                       return false;
+               }
+               return (din - dout) == (s.din - s.dout);
+       }
+}
diff --git a/T0/T0Comp.cs b/T0/T0Comp.cs
new file mode 100644 (file)
index 0000000..143badb
--- /dev/null
@@ -0,0 +1,2070 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+/*
+ * This is the main compiler class.
+ */
+
+public class T0Comp {
+
+       /*
+        * Command-line entry point.
+        */
+       public static void Main(string[] args)
+       {
+               try {
+                       List<string> r = new List<string>();
+                       string outBase = null;
+                       List<string> entryPoints = new List<string>();
+                       string coreRun = null;
+                       bool flow = true;
+                       int dsLim = 32;
+                       int rsLim = 32;
+                       for (int i = 0; i < args.Length; i ++) {
+                               string a = args[i];
+                               if (!a.StartsWith("-")) {
+                                       r.Add(a);
+                                       continue;
+                               }
+                               if (a == "--") {
+                                       for (;;) {
+                                               if (++ i >= args.Length) {
+                                                       break;
+                                               }
+                                               r.Add(args[i]);
+                                       }
+                                       break;
+                               }
+                               while (a.StartsWith("-")) {
+                                       a = a.Substring(1);
+                               }
+                               int j = a.IndexOf('=');
+                               string pname;
+                               string pval, pval2;
+                               if (j < 0) {
+                                       pname = a.ToLowerInvariant();
+                                       pval = null;
+                                       pval2 = (i + 1) < args.Length
+                                               ? args[i + 1] : null;
+                               } else {
+                                       pname = a.Substring(0, j).Trim()
+                                               .ToLowerInvariant();
+                                       pval = a.Substring(j + 1);
+                                       pval2 = null;
+                               }
+                               switch (pname) {
+                               case "o":
+                               case "out":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       if (outBase != null) {
+                                               Usage();
+                                       }
+                                       outBase = pval;
+                                       break;
+                               case "r":
+                               case "run":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       coreRun = pval;
+                                       break;
+                               case "m":
+                               case "main":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       foreach (string ep in pval.Split(',')) {
+                                               string epz = ep.Trim();
+                                               if (epz.Length > 0) {
+                                                       entryPoints.Add(epz);
+                                               }
+                                       }
+                                       break;
+                               case "nf":
+                               case "noflow":
+                                       flow = false;
+                                       break;
+                               default:
+                                       Usage();
+                                       break;
+                               }
+                       }
+                       if (r.Count == 0) {
+                               Usage();
+                       }
+                       if (outBase == null) {
+                               outBase = "t0out";
+                       }
+                       if (entryPoints.Count == 0) {
+                               entryPoints.Add("main");
+                       }
+                       if (coreRun == null) {
+                               coreRun = outBase;
+                       }
+                       T0Comp tc = new T0Comp();
+                       tc.enableFlowAnalysis = flow;
+                       tc.dsLimit = dsLim;
+                       tc.rsLimit = rsLim;
+                       using (TextReader tr = new StreamReader(
+                               Assembly.GetExecutingAssembly()
+                               .GetManifestResourceStream("t0-kernel")))
+                       {
+                               tc.ProcessInput(tr);
+                       }
+                       foreach (string a in r) {
+                               Console.WriteLine("[{0}]", a);
+                               using (TextReader tr = File.OpenText(a)) {
+                                       tc.ProcessInput(tr);
+                               }
+                       }
+                       tc.Generate(outBase, coreRun, entryPoints.ToArray());
+               } catch (Exception e) {
+                       Console.WriteLine(e.ToString());
+                       Environment.Exit(1);
+               }
+       }
+
+       static void Usage()
+       {
+               Console.WriteLine(
+"usage: T0Comp.exe [ options... ] file...");
+               Console.WriteLine(
+"options:");
+               Console.WriteLine(
+"   -o file    use 'file' as base for output file name (default: 't0out')");
+               Console.WriteLine(
+"   -r name    use 'name' as base for run function (default: same as output)");
+               Console.WriteLine(
+"   -m name[,name...]");
+               Console.WriteLine(
+"              define entry point(s)");
+               Console.WriteLine(
+"   -nf        disable flow analysis");
+               Environment.Exit(1);
+       }
+
+       /*
+        * If 'delayedChar' is Int32.MinValue then there is no delayed
+        * character.
+        * If 'delayedChar' equals x >= 0 then there is one delayed
+        * character of value x.
+        * If 'delayedChar' equals y < 0 then there are two delayed
+        * characters, a newline (U+000A) followed by character of
+        * value -(y+1).
+        */
+       TextReader currentInput;
+       int delayedChar;
+
+       /*
+        * Common StringBuilder used to parse tokens; it is reused for
+        * each new token.
+        */
+       StringBuilder tokenBuilder;
+
+       /*
+        * There may be a delayed token in some cases.
+        */
+       String delayedToken;
+
+       /*
+        * Defined words are referenced by name in this map. Names are
+        * string-sensitive; for better reproducibility, the map is sorted
+        * (ordinal order).
+        */
+       IDictionary<string, Word> words;
+
+       /*
+        * Last defined word is also referenced in 'lastWord'. This is
+        * used by 'immediate'.
+        */
+       Word lastWord;
+
+       /*
+        * When compiling, this builder is used. A stack saves other
+        * builders in case of nested definition.
+        */
+       WordBuilder wordBuilder;
+       Stack<WordBuilder> savedWordBuilders;
+
+       /*
+        * C code defined for words is kept in this map, by word name.
+        */
+       IDictionary<string, string> allCCode;
+
+       /*
+        * 'compiling' is true when compiling tokens to a word, false
+        * when interpreting them.
+        */
+       bool compiling;
+
+       /*
+        * 'quitRunLoop' is set to true to trigger exit of the
+        * interpretation loop when the end of the current input file
+        * is reached.
+        */
+       bool quitRunLoop;
+
+       /*
+        * 'extraCode' is for C code that is to be added as preamble to
+        * the C output.
+        */
+       List<string> extraCode;
+
+       /*
+        * 'dataBlock' is the data block in which constant data bytes
+        * are accumulated.
+        */
+       ConstData dataBlock;
+
+       /*
+        * Counter for blocks of constant data.
+        */
+       long currentBlobID;
+
+       /*
+        * Flow analysis enable flag.
+        */
+       bool enableFlowAnalysis;
+
+       /*
+        * Data stack size limit.
+        */
+       int dsLimit;
+
+       /*
+        * Return stack size limit.
+        */
+       int rsLimit;
+
+       T0Comp()
+       {
+               tokenBuilder = new StringBuilder();
+               words = new SortedDictionary<string, Word>(
+                       StringComparer.Ordinal);
+               savedWordBuilders = new Stack<WordBuilder>();
+               allCCode = new SortedDictionary<string, string>(
+                       StringComparer.Ordinal);
+               compiling = false;
+               extraCode = new List<string>();
+               enableFlowAnalysis = true;
+
+               /*
+                * Native words are predefined and implemented only with
+                * native code. Some may be part of the generated output,
+                * if C code is set for them.
+                */
+
+               /*
+                * add-cc:
+                * Parses next token as a word name, then a C code snippet.
+                * Sets the C code for that word.
+                */
+               AddNative("add-cc:", false, SType.BLANK, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       if (allCCode.ContainsKey(tt)) {
+                               throw new Exception(
+                                       "C code already set for: " + tt);
+                       }
+                       allCCode[tt] = ParseCCode();
+               });
+
+               /*
+                * cc:
+                * Parses next token as a word name, then a C code snippet.
+                * A new word is defined, that throws an exception when
+                * invoked during compilation. The C code is set for that
+                * new word.
+                */
+               AddNative("cc:", false, SType.BLANK, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       Word w = AddNative(tt, false, cpu2 => {
+                               throw new Exception(
+                                       "C-only word: " + tt);
+                       });
+                       if (allCCode.ContainsKey(tt)) {
+                               throw new Exception(
+                                       "C code already set for: " + tt);
+                       }
+                       SType stackEffect;
+                       allCCode[tt] = ParseCCode(out stackEffect);
+                       w.StackEffect = stackEffect;
+               });
+
+               /*
+                * preamble
+                * Parses a C code snippet, then adds it to the generated
+                * output preamble.
+                */
+               AddNative("preamble", false, SType.BLANK, cpu => {
+                       extraCode.Add(ParseCCode());
+               });
+
+               /*
+                * make-CX
+                * Expects two integers and a string, and makes a
+                * constant that stands for the string as a C constant
+                * expression. The two integers are the expected range
+                * (min-max, inclusive).
+                */
+               AddNative("make-CX", false, new SType(3, 1), cpu => {
+                       TValue c = cpu.Pop();
+                       if (!(c.ptr is TPointerBlob)) {
+                               throw new Exception(string.Format(
+                                       "'{0}' is not a string", c));
+                       }
+                       int max = cpu.Pop();
+                       int min = cpu.Pop();
+                       TValue tv = new TValue(0, new TPointerExpr(
+                               c.ToString(), min, max));
+                       cpu.Push(tv);
+               });
+
+               /*
+                * CX  (immediate)
+                * Parses two integer constants, then a C code snippet.
+                * It then pushes on the stack, or compiles to the
+                * current word, a value consisting of the given C
+                * expression; the two integers indicate the expected
+                * range (min-max, inclusive) of the C expression when
+                * evaluated.
+                */
+               AddNative("CX", true, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing min value)");
+                       }
+                       int min = ParseInteger(tt);
+                       tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing max value)");
+                       }
+                       int max = ParseInteger(tt);
+                       if (max < min) {
+                               throw new Exception("min/max in wrong order");
+                       }
+                       TValue tv = new TValue(0, new TPointerExpr(
+                               ParseCCode().Trim(), min, max));
+                       if (compiling) {
+                               wordBuilder.Literal(tv);
+                       } else {
+                               cpu.Push(tv);
+                       }
+               });
+
+               /*
+                * co
+                * Interrupt the current execution. This implements
+                * coroutines. It cannot be invoked during compilation.
+                */
+               AddNative("co", false, SType.BLANK, cpu => {
+                       throw new Exception("No coroutine in compile mode");
+               });
+
+               /*
+                * :
+                * Parses next token as word name. It begins definition
+                * of that word, setting it as current target for
+                * word building. Any previously opened word is saved
+                * and will become available again as a target when that
+                * new word is finished building.
+                */
+               AddNative(":", false, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       if (compiling) {
+                               savedWordBuilders.Push(wordBuilder);
+                       } else {
+                               compiling = true;
+                       }
+                       wordBuilder = new WordBuilder(this, tt);
+                       tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (while compiling)");
+                       }
+                       if (tt == "(") {
+                               SType stackEffect = ParseStackEffectNF();
+                               if (!stackEffect.IsKnown) {
+                                       throw new Exception(
+                                               "Invalid stack effect syntax");
+                               }
+                               wordBuilder.StackEffect = stackEffect;
+                       } else {
+                               delayedToken = tt;
+                       }
+               });
+
+               /*
+                * Pops a string as word name, and two integers as stack
+                * effect. It begins definition of that word, setting it
+                * as current target for word building. Any previously
+                * opened word is saved and will become available again as
+                * a target when that new word is finished building.
+                *
+                * Stack effect is the pair 'din dout'. If din is negative,
+                * then the stack effect is "unknown". If din is nonnegative
+                * but dout is negative, then the word is reputed never to
+                * return.
+                */
+               AddNative("define-word", false, cpu => {
+                       int dout = cpu.Pop();
+                       int din = cpu.Pop();
+                       TValue s = cpu.Pop();
+                       if (!(s.ptr is TPointerBlob)) {
+                               throw new Exception(string.Format(
+                                       "Not a string: '{0}'", s));
+                       }
+                       string tt = s.ToString();
+                       if (compiling) {
+                               savedWordBuilders.Push(wordBuilder);
+                       } else {
+                               compiling = true;
+                       }
+                       wordBuilder = new WordBuilder(this, tt);
+                       wordBuilder.StackEffect = new SType(din, dout);
+               });
+
+               /*
+                * ;  (immediate)
+                * Ends current word. The current word is registered under
+                * its name, and the previously opened word (if any) becomes
+                * again the building target.
+                */
+               AddNative(";", true, cpu => {
+                       if (!compiling) {
+                               throw new Exception("Not compiling");
+                       }
+                       Word w = wordBuilder.Build();
+                       string name = w.Name;
+                       if (words.ContainsKey(name)) {
+                               throw new Exception(
+                                       "Word already defined: " + name);
+                       }
+                       words[name] = w;
+                       lastWord = w;
+                       if (savedWordBuilders.Count > 0) {
+                               wordBuilder = savedWordBuilders.Pop();
+                       } else {
+                               wordBuilder = null;
+                               compiling = false;
+                       }
+               });
+
+               /*
+                * immediate
+                * Sets the last defined word as immediate.
+                */
+               AddNative("immediate", false, cpu => {
+                       if (lastWord == null) {
+                               throw new Exception("No word defined yet");
+                       }
+                       lastWord.Immediate = true;
+               });
+
+               /*
+                * literal  (immediate)
+                * Pops the current TOS value, and add in the current word
+                * the action of pushing that value. This cannot be used
+                * when no word is being built.
+                */
+               WordNative wliteral = AddNative("literal", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Literal(cpu.Pop());
+               });
+
+               /*
+                * compile
+                * Pops the current TOS value, which must be an XT (pointer
+                * to a word); the action of calling that word is compiled
+                * in the current word.
+                */
+               WordNative wcompile = AddNative("compile", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Call(cpu.Pop().ToXT());
+               });
+
+               /*
+                * postpone  (immediate)
+                * Parses the next token as a word name, and add to the
+                * current word the action of calling that word. This
+                * basically removes immediatety from the next word.
+                */
+               AddNative("postpone", true, cpu => {
+                       CheckCompiling();
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       TValue v;
+                       bool isVal = TryParseLiteral(tt, out v);
+                       Word w = LookupNF(tt);
+                       if (isVal && w != null) {
+                               throw new Exception(String.Format(
+                                       "Ambiguous: both defined word and"
+                                       + " literal: {0}", tt));
+                       }
+                       if (isVal) {
+                               wordBuilder.Literal(v);
+                               wordBuilder.CallExt(wliteral);
+                       } else if (w != null) {
+                               if (w.Immediate) {
+                                       wordBuilder.CallExt(w);
+                               } else {
+                                       wordBuilder.Literal(new TValue(0,
+                                               new TPointerXT(w)));
+                                       wordBuilder.CallExt(wcompile);
+                               }
+                       } else {
+                               wordBuilder.Literal(new TValue(0,
+                                       new TPointerXT(tt)));
+                               wordBuilder.CallExt(wcompile);
+                       }
+               });
+
+               /*
+                * Interrupt compilation with an error.
+                */
+               AddNative("exitvm", false, cpu => {
+                       throw new Exception();
+               });
+
+               /*
+                * Open a new data block. Its symbolic address is pushed
+                * on the stack.
+                */
+               AddNative("new-data-block", false, cpu => {
+                       dataBlock = new ConstData(this);
+                       cpu.Push(new TValue(0, new TPointerBlob(dataBlock)));
+               });
+
+               /*
+                * Define a new data word. The data address and name are
+                * popped from the stack.
+                */
+               AddNative("define-data-word", false, cpu => {
+                       string name = cpu.Pop().ToString();
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       Word w = new WordData(this, name, tb.Blob, va.x);
+                       if (words.ContainsKey(name)) {
+                               throw new Exception(
+                                       "Word already defined: " + name);
+                       }
+                       words[name] = w;
+                       lastWord = w;
+               });
+
+               /*
+                * Get an address pointing at the end of the current
+                * data block. This is the address of the next byte that
+                * will be added.
+                */
+               AddNative("current-data", false, cpu => {
+                       if (dataBlock == null) {
+                               throw new Exception(
+                                       "No current data block");
+                       }
+                       cpu.Push(new TValue(dataBlock.Length,
+                               new TPointerBlob(dataBlock)));
+               });
+
+               /*
+                * Add a byte value to the data block.
+                */
+               AddNative("data-add8", false, cpu => {
+                       if (dataBlock == null) {
+                               throw new Exception(
+                                       "No current data block");
+                       }
+                       int v = cpu.Pop();
+                       if (v < 0 || v > 0xFF) {
+                               throw new Exception(
+                                       "Byte value out of range: " + v);
+                       }
+                       dataBlock.Add8((byte)v);
+               });
+
+               /*
+                * Set a byte value in the data block.
+                */
+               AddNative("data-set8", false, cpu => {
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       int v = cpu.Pop();
+                       if (v < 0 || v > 0xFF) {
+                               throw new Exception(
+                                       "Byte value out of range: " + v);
+                       }
+                       tb.Blob.Set8(va.x, (byte)v);
+               });
+
+               /*
+                * Get a byte value from a data block.
+                */
+               AddNative("data-get8", false, new SType(1, 1), cpu => {
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       int v = tb.Blob.Read8(va.x);
+                       cpu.Push(v);
+               });
+
+               /*
+                *
+                */
+               AddNative("compile-local-read", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.GetLocal(cpu.Pop().ToString());
+               });
+               AddNative("compile-local-write", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.PutLocal(cpu.Pop().ToString());
+               });
+
+               AddNative("ahead", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Ahead();
+               });
+               AddNative("begin", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Begin();
+               });
+               AddNative("again", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Again();
+               });
+               AddNative("until", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AgainIfNot();
+               });
+               AddNative("untilnot", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AgainIf();
+               });
+               AddNative("if", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AheadIfNot();
+               });
+               AddNative("ifnot", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AheadIf();
+               });
+               AddNative("then", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Then();
+               });
+               AddNative("cs-pick", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.CSPick(cpu.Pop());
+               });
+               AddNative("cs-roll", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.CSRoll(cpu.Pop());
+               });
+               AddNative("next-word", false, cpu => {
+                       string s = Next();
+                       if (s == null) {
+                               throw new Exception("No next word (EOF)");
+                       }
+                       cpu.Push(StringToBlob(s));
+               });
+               AddNative("parse", false, cpu => {
+                       int d = cpu.Pop();
+                       string s = ReadTerm(d);
+                       cpu.Push(StringToBlob(s));
+               });
+               AddNative("char", false, cpu => {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception("No next character (EOF)");
+                       }
+                       cpu.Push(c);
+               });
+               AddNative("'", false, cpu => {
+                       string name = Next();
+                       cpu.Push(new TValue(0, new TPointerXT(name)));
+               });
+
+               /*
+                * The "execute" word is valid in generated C code, but
+                * since it jumps to a runtime pointer, its actual stack
+                * effect cannot be computed in advance.
+                */
+               AddNative("execute", false, cpu => {
+                       cpu.Pop().Execute(this, cpu);
+               });
+
+               AddNative("[", true, cpu => {
+                       CheckCompiling();
+                       compiling = false;
+               });
+               AddNative("]", false, cpu => {
+                       compiling = true;
+               });
+               AddNative("(local)", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.DefLocal(cpu.Pop().ToString());
+               });
+               AddNative("ret", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Ret();
+               });
+
+               AddNative("drop", false, new SType(1, 0), cpu => {
+                       cpu.Pop();
+               });
+               AddNative("dup", false, new SType(1, 2), cpu => {
+                       cpu.Push(cpu.Peek(0));
+               });
+               AddNative("swap", false, new SType(2, 2), cpu => {
+                       cpu.Rot(1);
+               });
+               AddNative("over", false, new SType(2, 3), cpu => {
+                       cpu.Push(cpu.Peek(1));
+               });
+               AddNative("rot", false, new SType(3, 3), cpu => {
+                       cpu.Rot(2);
+               });
+               AddNative("-rot", false, new SType(3, 3), cpu => {
+                       cpu.NRot(2);
+               });
+
+               /*
+                * "roll" and "pick" are special in that the stack slot
+                * they inspect might be known only at runtime, so an
+                * absolute stack effect cannot be attributed. Instead,
+                * we simply hope that the caller knows what it is doing,
+                * and we use a simple stack effect for just the count
+                * value and picked value.
+                */
+               AddNative("roll", false, new SType(1, 0), cpu => {
+                       cpu.Rot(cpu.Pop());
+               });
+               AddNative("pick", false, new SType(1, 1), cpu => {
+                       cpu.Push(cpu.Peek(cpu.Pop()));
+               });
+
+               AddNative("+", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       if (b.ptr == null) {
+                               a.x += (int)b;
+                               cpu.Push(a);
+                       } else if (a.ptr is TPointerBlob
+                               && b.ptr is TPointerBlob)
+                       {
+                               cpu.Push(StringToBlob(
+                                       a.ToString() + b.ToString()));
+                       } else {
+                               throw new Exception(string.Format(
+                                       "Cannot add '{0}' to '{1}'", b, a));
+                       }
+               });
+               AddNative("-", false, new SType(2, 1), cpu => {
+                       /*
+                        * We can subtract two pointers, provided that
+                        * they point to the same blob. Otherwise,
+                        * the subtraction second operand must be an
+                        * integer.
+                        */
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       TPointerBlob ap = a.ptr as TPointerBlob;
+                       TPointerBlob bp = b.ptr as TPointerBlob;
+                       if (ap != null && bp != null && ap.Blob == bp.Blob) {
+                               cpu.Push(new TValue(a.x - b.x));
+                               return;
+                       }
+                       int bx = b;
+                       a.x -= bx;
+                       cpu.Push(a);
+               });
+               AddNative("neg", false, new SType(1, 1), cpu => {
+                       int ax = cpu.Pop();
+                       cpu.Push(-ax);
+               });
+               AddNative("*", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax * bx);
+               });
+               AddNative("/", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax / bx);
+               });
+               AddNative("u/", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax / bx);
+               });
+               AddNative("%", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax % bx);
+               });
+               AddNative("u%", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax % bx);
+               });
+               AddNative("<", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax < bx);
+               });
+               AddNative("<=", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax <= bx);
+               });
+               AddNative(">", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax > bx);
+               });
+               AddNative(">=", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax >= bx);
+               });
+               AddNative("=", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       cpu.Push(a.Equals(b));
+               });
+               AddNative("<>", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       cpu.Push(!a.Equals(b));
+               });
+               AddNative("u<", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax < bx));
+               });
+               AddNative("u<=", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax <= bx));
+               });
+               AddNative("u>", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax > bx));
+               });
+               AddNative("u>=", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax >= bx);
+               });
+               AddNative("and", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax & bx);
+               });
+               AddNative("or", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax | bx);
+               });
+               AddNative("xor", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax ^ bx);
+               });
+               AddNative("not", false, new SType(1, 1), cpu => {
+                       uint ax = cpu.Pop();
+                       cpu.Push(~ax);
+               });
+               AddNative("<<", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax << count);
+               });
+               AddNative(">>", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       int ax = cpu.Pop();
+                       cpu.Push(ax >> count);
+               });
+               AddNative("u>>", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax >> count);
+               });
+
+               AddNative(".", false, new SType(1, 0), cpu => {
+                       Console.Write(" {0}", cpu.Pop().ToString());
+               });
+               AddNative(".s", false, SType.BLANK, cpu => {
+                       int n = cpu.Depth;
+                       for (int i = n - 1; i >= 0; i --) {
+                               Console.Write(" {0}", cpu.Peek(i).ToString());
+                       }
+               });
+               AddNative("putc", false, new SType(1, 0), cpu => {
+                       Console.Write((char)cpu.Pop());
+               });
+               AddNative("puts", false, new SType(1, 0), cpu => {
+                       Console.Write("{0}", cpu.Pop().ToString());
+               });
+               AddNative("cr", false, SType.BLANK, cpu => {
+                       Console.WriteLine();
+               });
+               AddNative("eqstr", false, new SType(2, 1), cpu => {
+                       string s2 = cpu.Pop().ToString();
+                       string s1 = cpu.Pop().ToString();
+                       cpu.Push(s1 == s2);
+               });
+       }
+
+       WordNative AddNative(string name, bool immediate,
+               WordNative.NativeRun code)
+       {
+               return AddNative(name, immediate, SType.UNKNOWN, code);
+       }
+
+       WordNative AddNative(string name, bool immediate, SType stackEffect,
+               WordNative.NativeRun code)
+       {
+               if (words.ContainsKey(name)) {
+                       throw new Exception(
+                               "Word already defined: " + name);
+               }
+               WordNative w = new WordNative(this, name, code);
+               w.Immediate = immediate;
+               w.StackEffect = stackEffect;
+               words[name] = w;
+               return w;
+       }
+
+       internal long NextBlobID()
+       {
+               return currentBlobID ++;
+       }
+
+       int NextChar()
+       {
+               int c = delayedChar;
+               if (c >= 0) {
+                       delayedChar = Int32.MinValue;
+               } else if (c > Int32.MinValue) {
+                       delayedChar = -(c + 1);
+                       c = '\n';
+               } else {
+                       c = currentInput.Read();
+               }
+               if (c == '\r') {
+                       if (delayedChar >= 0) {
+                               c = delayedChar;
+                               delayedChar = Int32.MinValue;
+                       } else {
+                               c = currentInput.Read();
+                       }
+                       if (c != '\n') {
+                               delayedChar = c;
+                               c = '\n';
+                       }
+               }
+               return c;
+       }
+
+       /*
+        * Un-read the character value 'c'. That value MUST be the one
+        * that was obtained from NextChar().
+        */
+       void Unread(int c)
+       {
+               if (c < 0) {
+                       return;
+               }
+               if (delayedChar < 0) {
+                       if (delayedChar != Int32.MinValue) {
+                               throw new Exception(
+                                       "Already two delayed characters");
+                       }
+                       delayedChar = c;
+               } else if (c != '\n') {
+                       throw new Exception("Cannot delay two characters");
+               } else {
+                       delayedChar = -(delayedChar + 1);
+               }
+       }
+
+       string Next()
+       {
+               string r = delayedToken;
+               if (r != null) {
+                       delayedToken = null;
+                       return r;
+               }
+               tokenBuilder.Length = 0;
+               int c;
+               for (;;) {
+                       c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       if (!IsWS(c)) {
+                               break;
+                       }
+               }
+               if (c == '"') {
+                       return ParseString();
+               }
+               for (;;) {
+                       tokenBuilder.Append((char)c);
+                       c = NextChar();
+                       if (c < 0 || IsWS(c)) {
+                               Unread(c);
+                               return tokenBuilder.ToString();
+                       }
+               }
+       }
+
+       string ParseCCode()
+       {
+               SType stackEffect;
+               string r = ParseCCode(out stackEffect);
+               if (stackEffect.IsKnown) {
+                       throw new Exception(
+                               "Stack effect forbidden in this declaration");
+               }
+               return r;
+       }
+
+       string ParseCCode(out SType stackEffect)
+       {
+               string s = ParseCCodeNF(out stackEffect);
+               if (s == null) {
+                       throw new Exception("Error while parsing C code");
+               }
+               return s;
+       }
+
+       string ParseCCodeNF(out SType stackEffect)
+       {
+               stackEffect = SType.UNKNOWN;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       if (!IsWS(c)) {
+                               if (c == '(') {
+                                       if (stackEffect.IsKnown) {
+                                               Unread(c);
+                                               return null;
+                                       }
+                                       stackEffect = ParseStackEffectNF();
+                                       if (!stackEffect.IsKnown) {
+                                               return null;
+                                       }
+                                       continue;
+                               } else if (c != '{') {
+                                       Unread(c);
+                                       return null;
+                               }
+                               break;
+                       }
+               }
+               StringBuilder sb = new StringBuilder();
+               int count = 1;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       switch (c) {
+                       case '{':
+                               count ++;
+                               break;
+                       case '}':
+                               if (-- count == 0) {
+                                       return sb.ToString();
+                               }
+                               break;
+                       }
+                       sb.Append((char)c);
+               }
+       }
+
+       /*
+        * Parse a stack effect declaration. This method assumes that the
+        * opening parenthesis has just been read. If the parsing fails,
+        * then this method returns SType.UNKNOWN.
+        */
+       SType ParseStackEffectNF()
+       {
+               bool seenSep = false;
+               bool seenBang = false;
+               int din = 0, dout = 0;
+               for (;;) {
+                       string t = Next();
+                       if (t == null) {
+                               return SType.UNKNOWN;
+                       }
+                       if (t == "--") {
+                               if (seenSep) {
+                                       return SType.UNKNOWN;
+                               }
+                               seenSep = true;
+                       } else if (t == ")") {
+                               if (seenSep) {
+                                       if (seenBang && dout == 1) {
+                                               dout = -1;
+                                       }
+                                       return new SType(din, dout);
+                               } else {
+                                       return SType.UNKNOWN;
+                               }
+                       } else {
+                               if (seenSep) {
+                                       if (dout == 0 && t == "!") {
+                                               seenBang = true;
+                                       }
+                                       dout ++;
+                               } else {
+                                       din ++;
+                               }
+                       }
+               }
+       }
+
+       string ParseString()
+       {
+               StringBuilder sb = new StringBuilder();
+               sb.Append('"');
+               bool lcwb = false;
+               int hexNum = 0;
+               int acc = 0;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception(
+                                       "Unfinished literal string");
+                       }
+                       if (hexNum > 0) {
+                               int d = HexVal(c);
+                               if (d < 0) {
+                                       throw new Exception(String.Format(
+                                               "not an hex digit: U+{0:X4}",
+                                               c));
+                               }
+                               acc = (acc << 4) + d;
+                               if (-- hexNum == 0) {
+                                       sb.Append((char)acc);
+                                       acc = 0;
+                               }
+                       } else if (lcwb) {
+                               lcwb = false;
+                               switch (c) {
+                               case '\n': SkipNL(); break;
+                               case 'x':
+                                       hexNum = 2;
+                                       break;
+                               case 'u':
+                                       hexNum = 4;
+                                       break;
+                               default:
+                                       sb.Append(SingleCharEscape(c));
+                                       break;
+                               }
+                       } else {
+                               switch (c) {
+                               case '"':
+                                       return sb.ToString();
+                               case '\\':
+                                       lcwb = true;
+                                       break;
+                               default:
+                                       sb.Append((char)c);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       static char SingleCharEscape(int c)
+       {
+               switch (c) {
+               case 'n': return '\n';
+               case 'r': return '\r';
+               case 't': return '\t';
+               case 's': return ' ';
+               default:
+                       return (char)c;
+               }
+       }
+
+       /*
+        * A backslash+newline sequence occurred in a literal string; we
+        * check and consume the newline escape sequence (whitespace at
+        * start of next line, then a double-quote character).
+        */
+       void SkipNL()
+       {
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception("EOF in literal string");
+                       }
+                       if (c == '\n') {
+                               throw new Exception(
+                                       "Unescaped newline in literal string");
+                       }
+                       if (IsWS(c)) {
+                               continue;
+                       }
+                       if (c == '"') {
+                               return;
+                       }
+                       throw new Exception(
+                               "Invalid newline escape in literal string");
+               }
+       }
+
+       static char DecodeCharConst(string t)
+       {
+               if (t.Length == 1 && t[0] != '\\') {
+                       return t[0];
+               }
+               if (t.Length >= 2 && t[0] == '\\') {
+                       switch (t[1]) {
+                       case 'x':
+                               if (t.Length == 4) {
+                                       int x = DecHex(t.Substring(2));
+                                       if (x >= 0) {
+                                               return (char)x;
+                                       }
+                               }
+                               break;
+                       case 'u':
+                               if (t.Length == 6) {
+                                       int x = DecHex(t.Substring(2));
+                                       if (x >= 0) {
+                                               return (char)x;
+                                       }
+                               }
+                               break;
+                       default:
+                               if (t.Length == 2) {
+                                       return SingleCharEscape(t[1]);
+                               }
+                               break;
+                       }
+               }
+               throw new Exception("Invalid literal char: `" + t);
+       }
+
+       static int DecHex(string s)
+       {
+               int acc = 0;
+               foreach (char c in s) {
+                       int d = HexVal(c);
+                       if (d < 0) {
+                               return -1;
+                       }
+                       acc = (acc << 4) + d;
+               }
+               return acc;
+       }
+
+       static int HexVal(int c)
+       {
+               if (c >= '0' && c <= '9') {
+                       return c - '0';
+               } else if (c >= 'A' && c <= 'F') {
+                       return c - ('A' - 10);
+               } else if (c >= 'a' && c <= 'f') {
+                       return c - ('a' - 10);
+               } else {
+                       return -1;
+               }
+       }
+
+       string ReadTerm(int ct)
+       {
+               StringBuilder sb = new StringBuilder();
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception(String.Format(
+                                       "EOF reached before U+{0:X4}", ct));
+                       }
+                       if (c == ct) {
+                               return sb.ToString();
+                       }
+                       sb.Append((char)c);
+               }
+       }
+
+       static bool IsWS(int c)
+       {
+               return c <= 32;
+       }
+
+       void ProcessInput(TextReader tr)
+       {
+               this.currentInput = tr;
+               delayedChar = -1;
+               Word w = new WordNative(this, "toplevel",
+                       xcpu => { CompileStep(xcpu); });
+               CPU cpu = new CPU();
+               Opcode[] code = new Opcode[] {
+                       new OpcodeCall(w),
+                       new OpcodeJumpUncond(-2)
+               };
+               quitRunLoop = false;
+               cpu.Enter(code, 0);
+               for (;;) {
+                       if (quitRunLoop) {
+                               break;
+                       }
+                       Opcode op = cpu.ipBuf[cpu.ipOff ++];
+                       op.Run(cpu);
+               }
+       }
+
+       void CompileStep(CPU cpu)
+       {
+               string tt = Next();
+               if (tt == null) {
+                       if (compiling) {
+                               throw new Exception("EOF while compiling");
+                       }
+                       quitRunLoop = true;
+                       return;
+               }
+               TValue v;
+               bool isVal = TryParseLiteral(tt, out v);
+               Word w = LookupNF(tt);
+               if (isVal && w != null) {
+                       throw new Exception(String.Format(
+                               "Ambiguous: both defined word and literal: {0}",
+                               tt));
+               }
+               if (compiling) {
+                       if (isVal) {
+                               wordBuilder.Literal(v);
+                       } else if (w != null) {
+                               if (w.Immediate) {
+                                       w.Run(cpu);
+                               } else {
+                                       wordBuilder.CallExt(w);
+                               }
+                       } else {
+                               wordBuilder.Call(tt);
+                       }
+               } else {
+                       if (isVal) {
+                               cpu.Push(v);
+                       } else if (w != null) {
+                               w.Run(cpu);
+                       } else {
+                               throw new Exception(String.Format(
+                                       "Unknown word: '{0}'", tt));
+                       }
+               }
+       }
+
+       string GetCCode(string name)
+       {
+               string ccode;
+               allCCode.TryGetValue(name, out ccode);
+               return ccode;
+       }
+
+       void Generate(string outBase, string coreRun,
+               params string[] entryPoints)
+       {
+               /*
+                * Gather all words that are part of the generated
+                * code. This is done by exploring references
+                * transitively. All such words are thus implicitly
+                * resolved.
+                */
+               IDictionary<string, Word> wordSet =
+                       new SortedDictionary<string, Word>(
+                               StringComparer.Ordinal);
+               Queue<Word> tx = new Queue<Word>();
+               foreach (string ep in entryPoints) {
+                       if (wordSet.ContainsKey(ep)) {
+                               continue;
+                       }
+                       Word w = Lookup(ep);
+                       wordSet[w.Name] = w;
+                       tx.Enqueue(w);
+               }
+               while (tx.Count > 0) {
+                       Word w = tx.Dequeue();
+                       foreach (Word w2 in w.GetReferences()) {
+                               if (wordSet.ContainsKey(w2.Name)) {
+                                       continue;
+                               }
+                               wordSet[w2.Name] = w2;
+                               tx.Enqueue(w2);
+                       }
+               }
+
+               /*
+                * Do flow analysis.
+                */
+               if (enableFlowAnalysis) {
+                       foreach (string ep in entryPoints) {
+                               Word w = wordSet[ep];
+                               w.AnalyseFlow();
+                               Console.WriteLine("{0}: ds={1} rs={2}",
+                                       ep, w.MaxDataStack, w.MaxReturnStack);
+                               if (w.MaxDataStack > dsLimit) {
+                                       throw new Exception("'" + ep
+                                               + "' exceeds data stack limit");
+                               }
+                               if (w.MaxReturnStack > rsLimit) {
+                                       throw new Exception("'" + ep
+                                               + "' exceeds return stack"
+                                               + " limit");
+                               }
+                       }
+               }
+
+               /*
+                * Gather referenced data areas and compute their
+                * addresses in the generated data block. The address
+                * 0 in the data block is unaffected so that no
+                * valid runtime pointer is equal to null.
+                */
+               IDictionary<long, ConstData> blocks =
+                       new SortedDictionary<long, ConstData>();
+               foreach (Word w in wordSet.Values) {
+                       foreach (ConstData cd in w.GetDataBlocks()) {
+                               blocks[cd.ID] = cd;
+                       }
+               }
+               int dataLen = 1;
+               foreach (ConstData cd in blocks.Values) {
+                       cd.Address = dataLen;
+                       dataLen += cd.Length;
+               }
+
+               /*
+                * Generated code is a sequence of "slot numbers", each
+                * referencing either a piece of explicit C code, or an
+                * entry in the table of interpreted words.
+                *
+                * Opcodes other than "call" get the slots 0 to 6:
+                *
+                *   0   ret           no argument
+                *   1   const         signed value
+                *   2   get local     local number
+                *   3   put local     local number
+                *   4   jump          signed offset
+                *   5   jump if       signed offset
+                *   6   jump if not   signed offset
+                *
+                * The argument, if any, is in "7E" format: the value is
+                * encoded in 7-bit chunk, with big-endian signed
+                * convention. Each 7-bit chunk is encoded over one byte;
+                * the upper bit is 1 for all chunks except the last one.
+                *
+                * Words with explicit C code get the slot numbers
+                * immediately after 6. Interpreted words come afterwards.
+                */
+               IDictionary<string, int> slots = new Dictionary<string, int>();
+               int curSlot = 7;
+
+               /*
+                * Get explicit C code for words which have such code.
+                * We use string equality on C code so that words with
+                * identical implementations get merged.
+                *
+                * We also check that words with no explicit C code are
+                * interpreted.
+                */
+               IDictionary<string, int> ccodeUni =
+                       new Dictionary<string, int>();
+               IDictionary<int, string> ccodeNames =
+                       new Dictionary<int, string>();
+               foreach (Word w in wordSet.Values) {
+                       string ccode = GetCCode(w.Name);
+                       if (ccode == null) {
+                               if (w is WordNative) {
+                                       throw new Exception(String.Format(
+                                               "No C code for native '{0}'",
+                                               w.Name));
+                               }
+                               continue;
+                       }
+                       int sn;
+                       if (ccodeUni.ContainsKey(ccode)) {
+                               sn = ccodeUni[ccode];
+                               ccodeNames[sn] += " " + EscapeCComment(w.Name);
+                       } else {
+                               sn = curSlot ++;
+                               ccodeUni[ccode] = sn;
+                               ccodeNames[sn] = EscapeCComment(w.Name);
+                       }
+                       slots[w.Name] = sn;
+                       w.Slot = sn;
+               }
+
+               /*
+                * Assign slot values to all remaining words; we know they
+                * are all interpreted.
+                */
+               int slotInterpreted = curSlot;
+               foreach (Word w in wordSet.Values) {
+                       if (GetCCode(w.Name) != null) {
+                               continue;
+                       }
+                       int sn = curSlot ++;
+                       slots[w.Name] = sn;
+                       w.Slot = sn;
+               }
+               int numInterpreted = curSlot - slotInterpreted;
+
+               /*
+                * Verify that all entry points are interpreted words.
+                */
+               foreach (string ep in entryPoints) {
+                       if (GetCCode(ep) != null) {
+                               throw new Exception(
+                                       "Non-interpreted entry point");
+                       }
+               }
+
+               /*
+                * Compute the code block. Each word (without any C code)
+                * yields some CodeElement instances.
+                */
+               List<CodeElement> gcodeList = new List<CodeElement>();
+               CodeElement[] interpretedEntry =
+                       new CodeElement[numInterpreted];
+               foreach (Word w in wordSet.Values) {
+                       if (GetCCode(w.Name) != null) {
+                               continue;
+                       }
+                       int n = gcodeList.Count;
+                       w.GenerateCodeElements(gcodeList);
+                       interpretedEntry[w.Slot - slotInterpreted] =
+                               gcodeList[n];
+               }
+               CodeElement[] gcode = gcodeList.ToArray();
+
+               /*
+                * Compute all addresses and offsets. This loops until
+                * the addresses stabilize.
+                */
+               int totalLen = -1;
+               int[] gcodeLen = new int[gcode.Length];
+               for (;;) {
+                       for (int i = 0; i < gcode.Length; i ++) {
+                               gcodeLen[i] = gcode[i].Length;
+                       }
+                       int off = 0;
+                       for (int i = 0; i < gcode.Length; i ++) {
+                               gcode[i].Address = off;
+                               gcode[i].LastLength = gcodeLen[i];
+                               off += gcodeLen[i];
+                       }
+                       if (off == totalLen) {
+                               break;
+                       }
+                       totalLen = off;
+               }
+
+               /*
+                * Produce output file.
+                */
+               using (TextWriter tw = File.CreateText(outBase + ".c")) {
+                       tw.NewLine = "\n";
+
+                       tw.WriteLine("{0}",
+@"/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+");
+
+                       /*
+                        * Add declarations (not definitions) for the
+                        * entry point initialisation functions, and the
+                        * runner.
+                        */
+                       tw.WriteLine();
+                       foreach (string ep in entryPoints) {
+                               tw.WriteLine("void {0}_init_{1}(void *t0ctx);",
+                                       coreRun, ep);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("void {0}_run(void *t0ctx);", coreRun);
+
+                       /*
+                        * Add preamble elements here. They may be needed
+                        * for evaluating constant expressions in the
+                        * code block.
+                        */
+                       foreach (string pp in extraCode) {
+                               tw.WriteLine();
+                               tw.WriteLine("{0}", pp);
+                       }
+
+                       BlobWriter bw;
+                       tw.WriteLine();
+                       tw.Write("static const uint8_t t0_datablock[] = {");
+                       bw = new BlobWriter(tw, 78, 1);
+                       bw.Append((byte)0);
+                       foreach (ConstData cd in blocks.Values) {
+                               cd.Encode(bw);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.Write("static const uint8_t t0_codeblock[] = {");
+                       bw = new BlobWriter(tw, 78, 1);
+                       foreach (CodeElement ce in gcode) {
+                               ce.Encode(bw);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.Write("static const uint16_t t0_caddr[] = {");
+                       for (int i = 0; i < interpretedEntry.Length; i ++) {
+                               if (i != 0) {
+                                       tw.Write(',');
+                               }
+                               tw.WriteLine();
+                               tw.Write("\t{0}", interpretedEntry[i].Address);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.WriteLine("#define T0_INTERPRETED   {0}",
+                               slotInterpreted);
+                       tw.WriteLine();
+                       tw.WriteLine("{0}",
+@"#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)");
+                       tw.WriteLine();
+                       tw.WriteLine("{0}",
+@"#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}");
+
+                       tw.WriteLine();
+                       foreach (string ep in entryPoints) {
+                               tw.WriteLine("T0_DEFENTRY({0}, {1})",
+                                       coreRun + "_init_" + ep,
+                                       wordSet[ep].Slot);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("void");
+                       tw.WriteLine("{0}_run(void *t0ctx)", coreRun);
+                       tw.WriteLine("{0}",
+@"{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;");
+
+                       SortedDictionary<int, string> nccode =
+                               new SortedDictionary<int, string>();
+                       foreach (string k in ccodeUni.Keys) {
+                               nccode[ccodeUni[k]] = k;
+                       }
+                       foreach (int sn in nccode.Keys) {
+                               tw.WriteLine(
+@"                     case {0}: {{
+                               /* {1} */
+{2}
+                               }}
+                               break;", sn, ccodeNames[sn], nccode[sn]);
+                       }
+
+                       tw.WriteLine(
+@"                     }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}");
+               }
+
+               int codeLen = 0;
+               foreach (CodeElement ce in gcode) {
+                       codeLen += ce.Length;
+               }
+               int dataBlockLen = 0;
+               foreach (ConstData cd in blocks.Values) {
+                       dataBlockLen += cd.Length;
+               }
+
+               /*
+                * Write some statistics on produced code.
+                */
+               Console.WriteLine("code length: {0,6} byte(s)", codeLen);
+               Console.WriteLine("data length: {0,6} byte(s)", dataLen);
+               Console.WriteLine("interpreted words: {0}",
+                       interpretedEntry.Length);
+       }
+
+       internal Word Lookup(string name)
+       {
+               Word w = LookupNF(name);
+               if (w != null) {
+                       return w;
+               }
+               throw new Exception(String.Format("No such word: '{0}'", name));
+       }
+
+       internal Word LookupNF(string name)
+       {
+               Word w;
+               words.TryGetValue(name, out w);
+               return w;
+       }
+
+       internal TValue StringToBlob(string s)
+       {
+               return new TValue(0, new TPointerBlob(this, s));
+       }
+
+       internal bool TryParseLiteral(string tt, out TValue tv)
+       {
+               tv = new TValue(0);
+               if (tt.StartsWith("\"")) {
+                       tv = StringToBlob(tt.Substring(1));
+                       return true;
+               }
+               if (tt.StartsWith("`")) {
+                       tv = DecodeCharConst(tt.Substring(1));
+                       return true;
+               }
+               bool neg = false;
+               if (tt.StartsWith("-")) {
+                       neg = true;
+                       tt = tt.Substring(1);
+               } else if (tt.StartsWith("+")) {
+                       tt = tt.Substring(1);
+               }
+               uint radix = 10;
+               if (tt.StartsWith("0x") || tt.StartsWith("0X")) {
+                       radix = 16;
+                       tt = tt.Substring(2);
+               } else if (tt.StartsWith("0b") || tt.StartsWith("0B")) {
+                       radix = 2;
+                       tt = tt.Substring(2);
+               }
+               if (tt.Length == 0) {
+                       return false;
+               }
+               uint acc = 0;
+               bool overflow = false;
+               uint maxV = uint.MaxValue / radix;
+               foreach (char c in tt) {
+                       int d = HexVal(c);
+                       if (d < 0 || d >= radix) {
+                               return false;
+                       }
+                       if (acc > maxV) {
+                               overflow = true;
+                       }
+                       acc *= radix;
+                       if ((uint)d > uint.MaxValue - acc) {
+                               overflow = true;
+                       }
+                       acc += (uint)d;
+               }
+               int x = (int)acc;
+               if (neg) {
+                       if (acc > (uint)0x80000000) {
+                               overflow = true;
+                       }
+                       x = -x;
+               }
+               if (overflow) {
+                       throw new Exception(
+                               "invalid literal integer (overflow)");
+               }
+               tv = x;
+               return true;
+       }
+
+       int ParseInteger(string tt)
+       {
+               TValue tv;
+               if (!TryParseLiteral(tt, out tv)) {
+                       throw new Exception("not an integer: " + ToString());
+               }
+               return (int)tv;
+       }
+
+       void CheckCompiling()
+       {
+               if (!compiling) {
+                       throw new Exception("Not in compilation mode");
+               }
+       }
+
+       static string EscapeCComment(string s)
+       {
+               StringBuilder sb = new StringBuilder();
+               foreach (char c in s) {
+                       if (c >= 33 && c <= 126 && c != '%') {
+                               sb.Append(c);
+                       } else if (c < 0x100) {
+                               sb.AppendFormat("%{0:X2}", (int)c);
+                       } else if (c < 0x800) {
+                               sb.AppendFormat("%{0:X2}%{0:X2}",
+                                       ((int)c >> 6) | 0xC0,
+                                       ((int)c & 0x3F) | 0x80);
+                       } else {
+                               sb.AppendFormat("%{0:X2}%{0:X2}%{0:X2}",
+                                       ((int)c >> 12) | 0xE0,
+                                       (((int)c >> 6) & 0x3F) | 0x80,
+                                       ((int)c & 0x3F) | 0x80);
+                       }
+               }
+               return sb.ToString().Replace("*/", "%2A/");
+       }
+}
diff --git a/T0/TPointerBase.cs b/T0/TPointerBase.cs
new file mode 100644 (file)
index 0000000..f996d8d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerBase {
+
+       /* obsolete
+       internal virtual TValue Get(TValue vp)
+       {
+               throw new Exception(
+                       "cannot get values from this pointer");
+       }
+
+       internal virtual void Set(TValue vp, TValue nval)
+       {
+               throw new Exception(
+                       "cannot set values to this pointer");
+       }
+       */
+
+       internal virtual bool ToBool(TValue vp)
+       {
+               return true;
+       }
+
+       internal virtual void Execute(T0Comp ctx, CPU cpu)
+       {
+               throw new Exception("value is not an xt: " + ToString());
+       }
+
+       internal virtual string ToString(TValue vp)
+       {
+               return String.Format("{0}+{1}",
+                       GetType().Name, vp.x);
+       }
+
+       internal virtual bool Equals(TPointerBase tp)
+       {
+               return this == tp;
+       }
+}
diff --git a/T0/TPointerBlob.cs b/T0/TPointerBlob.cs
new file mode 100644 (file)
index 0000000..d4aeff6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerBlob : TPointerBase {
+
+       internal ConstData Blob { get; private set; }
+
+       internal TPointerBlob(ConstData cd)
+       {
+               this.Blob = cd;
+       }
+
+       internal TPointerBlob(T0Comp owner, string s)
+       {
+               Blob = new ConstData(owner);
+               Blob.AddString(s);
+       }
+
+       /* obsolete
+       internal override TValue Get8(TValue vp)
+       {
+               return new TValue((int)Blob.Read8(vp.x));
+       }
+
+       internal override TValue Get16(TValue vp)
+       {
+               return new TValue((int)Blob.Read16(vp.x));
+       }
+
+       internal override TValue Get24(TValue vp)
+       {
+               return new TValue((int)Blob.Read24(vp.x));
+       }
+
+       internal override TValue Get32(TValue vp)
+       {
+               return new TValue((int)Blob.Read32(vp.x));
+       }
+       */
+
+       internal override string ToString(TValue vp)
+       {
+               return Blob.ToString(vp.x);
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               TPointerBlob tb = tp as TPointerBlob;
+               return tb != null && Blob == tb.Blob;
+       }
+}
diff --git a/T0/TPointerExpr.cs b/T0/TPointerExpr.cs
new file mode 100644 (file)
index 0000000..314b272
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class TPointerExpr : TPointerBase {
+
+       string expr;
+       int min, max;
+
+       internal TPointerExpr(string expr, int min, int max)
+       {
+               this.expr = expr;
+               this.min = min;
+               this.max = max;
+       }
+
+       internal override bool ToBool(TValue vp)
+       {
+               throw new Exception("Cannot evaluate C-expr at compile time");
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return ToCExpr(vp.x);
+       }
+
+       internal string ToCExpr(int off)
+       {
+               if (off == 0) {
+                       return expr;
+               } else if (off > 0) {
+                       return String.Format(
+                               "(uint32_t)({0}) + {1}", expr, off);
+               } else {
+                       return String.Format(
+                               "(uint32_t)({0}) - {1}", expr, -(long)off);
+               }
+       }
+
+       internal int GetMaxBitLength(int off)
+       {
+               long rmin = (long)min + off;
+               long rmax = (long)max + off;
+               int numBits = 1;
+               if (rmin < 0) {
+                       numBits = Math.Max(numBits, BitLength(rmin));
+               }
+               if (rmax > 0) {
+                       numBits = Math.Max(numBits, BitLength(rmax));
+               }
+               return Math.Min(numBits, 32);
+       }
+
+       /*
+        * Get the minimal bit length of a value. This is for a signed
+        * representation: the length includes a sign bit. Thus, the
+        * returned value will be at least 1.
+        */
+       static int BitLength(long v)
+       {
+               int num = 1;
+               if (v < 0) {
+                       while (v != -1) {
+                               num ++;
+                               v >>= 1;
+                       }
+               } else {
+                       while (v != 0) {
+                               num ++;
+                               v >>= 1;
+                       }
+               }
+               return num;
+       }
+}
diff --git a/T0/TPointerNull.cs b/T0/TPointerNull.cs
new file mode 100644 (file)
index 0000000..f8eb11e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerNull : TPointerBase {
+
+       internal override bool ToBool(TValue vp)
+       {
+               return false;
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return "null";
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               return tp is TPointerNull;
+       }
+}
diff --git a/T0/TPointerXT.cs b/T0/TPointerXT.cs
new file mode 100644 (file)
index 0000000..883f312
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerXT : TPointerBase {
+
+       internal string Name {
+               get; private set;
+       }
+
+       internal Word Target {
+               get; private set;
+       }
+
+       internal TPointerXT(string name)
+       {
+               this.Name = name;
+               this.Target = null;
+       }
+
+       internal TPointerXT(Word target)
+       {
+               this.Name = target.Name;
+               this.Target = target;
+       }
+
+       internal void Resolve(T0Comp ctx)
+       {
+               if (Target == null) {
+                       Target = ctx.Lookup(Name);
+               }
+       }
+
+       internal override void Execute(T0Comp ctx, CPU cpu)
+       {
+               Resolve(ctx);
+               Target.Run(cpu);
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return String.Format("<'{0}>", Name);
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               TPointerXT tx = tp as TPointerXT;
+               return tx != null && Name == tx.Name;
+       }
+}
diff --git a/T0/TValue.cs b/T0/TValue.cs
new file mode 100644 (file)
index 0000000..e4f2255
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * Each value is represented with a TValue structure. Integers use the 'x'
+ * field, and 'ptr' is null; for pointers, the 'ptr' field is used, and the
+ * 'x' is then an offset in the object represented by 'ptr'.
+ */
+
+struct TValue {
+
+       internal int x;
+       internal TPointerBase ptr;
+
+       internal TValue(int x)
+       {
+               this.x = x;
+               this.ptr = null;
+       }
+
+       internal TValue(uint x)
+       {
+               this.x = (int)x;
+               this.ptr = null;
+       }
+
+       internal TValue(bool b)
+       {
+               this.x = b ? -1 : 0;
+               this.ptr = null;
+       }
+
+       internal TValue(int x, TPointerBase ptr)
+       {
+               this.x = x;
+               this.ptr = ptr;
+       }
+
+       /*
+        * Convert this value to a boolean; integer 0 and null pointer are
+        * 'false', other values are 'true'.
+        */
+       internal bool Bool {
+               get {
+                       if (ptr == null) {
+                               return x != 0;
+                       } else {
+                               return ptr.ToBool(this);
+                       }
+               }
+       }
+
+       /*
+        * Get this value as an integer. Pointers cannot be converted to
+        * integers.
+        */
+       internal int Int {
+               get {
+                       if (ptr == null) {
+                               return x;
+                       }
+                       throw new Exception("not an integer: " + ToString());
+               }
+       }
+
+       /*
+        * Get this value as an unsigned integer. This is the integer
+        * value, reduced modulo 2^32 in the 0..2^32-1 range.
+        */
+       internal uint UInt {
+               get {
+                       return (uint)Int;
+               }
+       }
+
+       /*
+        * String format of integers uses decimal representation. For
+        * pointers, this depends on the pointed-to value.
+        */
+       public override string ToString()
+       {
+               if (ptr == null) {
+                       return String.Format("{0}", x);
+               } else {
+                       return ptr.ToString(this);
+               }
+       }
+
+       /*
+        * If this value is an XT, then execute it. Otherwise, an exception
+        * is thrown.
+        */
+       internal void Execute(T0Comp ctx, CPU cpu)
+       {
+               ToXT().Execute(ctx, cpu);
+       }
+
+       /*
+        * Convert this value to an XT. On failure, an exception is thrown.
+        */
+       internal TPointerXT ToXT()
+       {
+               TPointerXT xt = ptr as TPointerXT;
+               if (xt == null) {
+                       throw new Exception(
+                               "value is not an xt: " + ToString());
+               }
+               return xt;
+       }
+
+       /*
+        * Compare this value to another.
+        */
+       internal bool Equals(TValue v)
+       {
+               if (x != v.x) {
+                       return false;
+               }
+               if (ptr == v.ptr) {
+                       return true;
+               }
+               if (ptr == null || v.ptr == null) {
+                       return false;
+               }
+               return ptr.Equals(v.ptr);
+       }
+
+       public static implicit operator TValue(bool val)
+       {
+               return new TValue(val);
+       }
+
+       public static implicit operator TValue(sbyte val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(byte val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(short val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(ushort val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(char val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(int val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(uint val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator bool(TValue v)
+       {
+               return v.Bool;
+       }
+
+       public static implicit operator sbyte(TValue v)
+       {
+               return (sbyte)v.Int;
+       }
+
+       public static implicit operator byte(TValue v)
+       {
+               return (byte)v.Int;
+       }
+
+       public static implicit operator short(TValue v)
+       {
+               return (short)v.Int;
+       }
+
+       public static implicit operator ushort(TValue v)
+       {
+               return (ushort)v.Int;
+       }
+
+       public static implicit operator char(TValue v)
+       {
+               return (char)v.Int;
+       }
+
+       public static implicit operator int(TValue v)
+       {
+               return (int)v.Int;
+       }
+
+       public static implicit operator uint(TValue v)
+       {
+               return (uint)v.Int;
+       }
+}
diff --git a/T0/Word.cs b/T0/Word.cs
new file mode 100644 (file)
index 0000000..2dfb66e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * A "word" is a function with a name. Words can be either native or
+ * interpreted; native words are implemented as some in-compiler special
+ * code.
+ *
+ * Some native words (not all of them) have a C implementation and can
+ * thus be part of the generated C code. Native words with no C
+ * implementation can be used only during compilation; this is typically
+ * the case for words that support the syntax (e.g. 'if').
+ */
+
+abstract class Word {
+
+       /*
+        * The compiler context for this word.
+        */
+       internal T0Comp TC {
+               get; private set;
+       }
+
+       /*
+        * Immediate words are executed immediately when encountered in the
+        * source code, even while compiling another word.
+        */
+       internal bool Immediate {
+               get; set;
+       }
+
+       /*
+        * Each word has a unique name. Names are case-sensitive.
+        */
+       internal string Name {
+               get; private set;
+       }
+
+       /*
+        * Words are allocated slot numbers when output code is generated.
+        */
+       internal int Slot {
+               get; set;
+       }
+
+       /*
+        * Each word may have a known stack effect.
+        */
+       internal SType StackEffect {
+               get; set;
+       }
+
+       internal Word(T0Comp owner, string name)
+       {
+               TC = owner;
+               Name = name;
+               StackEffect = SType.UNKNOWN;
+       }
+
+       /*
+        * Resolving a word means looking up all references to external
+        * words.
+        */
+       internal virtual void Resolve()
+       {
+       }
+
+       /*
+        * Execute this word. If the word is native, then its code is
+        * run right away; if the word is interpreted, then the entry
+        * sequence is executed.
+        */
+       internal virtual void Run(CPU cpu)
+       {
+               throw new Exception(String.Format(
+                       "cannot run '{0}' at compile-time", Name));
+       }
+
+       /*
+        * All words may have an explicit C implementations. To be part
+        * of the generated C code, a word must either be interpreted,
+        * or have an explicit C implementation, or both.
+        */
+       internal string CCode {
+               get; set;
+       }
+
+       /*
+        * Get all words referenced from this one. This implies
+        * resolving the word.
+        */
+       internal virtual List<Word> GetReferences()
+       {
+               return new List<Word>();
+       }
+
+       /*
+        * Get all data blocks directly referenced from this one. This
+        * implies resolving the word.
+        */
+       internal virtual List<ConstData> GetDataBlocks()
+       {
+               return new List<ConstData>();
+       }
+
+       /*
+        * Produce the code elements for this word.
+        */
+       internal virtual void GenerateCodeElements(List<CodeElement> dst)
+       {
+               throw new Exception("Word does not yield code elements");
+       }
+
+       /*
+        * Compute/verify stack effect for this word.
+        */
+       internal virtual void AnalyseFlow()
+       {
+       }
+
+       /*
+        * Get maximum data stack usage for this word. This is the number
+        * of extra slots that this word may need on the data stack. If
+        * the stack effect is not known, this returns -1.
+        */
+       internal virtual int MaxDataStack {
+               get {
+                       SType se = StackEffect;
+                       if (!se.IsKnown) {
+                               return -1;
+                       }
+                       if (se.NoExit) {
+                               return 0;
+                       } else {
+                               return Math.Min(0, se.DataOut - se.DataIn);
+                       }
+               }
+       }
+
+       /*
+        * Get maximum return stack usage for this word.
+        */
+       internal virtual int MaxReturnStack {
+               get {
+                       return 0;
+               }
+       }
+}
diff --git a/T0/WordBuilder.cs b/T0/WordBuilder.cs
new file mode 100644 (file)
index 0000000..c410930
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * A WordBuilder instance organizes construction of a new interpreted word.
+ *
+ * Opcodes are accumulated with specific methods. A control-flow stack
+ * is maintained to resolve jumps.
+ *
+ * Each instance shall be used for only one word.
+ */
+
+class WordBuilder {
+
+       T0Comp TC;
+       string name;
+       int[] cfStack;
+       int cfPtr;
+       List<Opcode> code;
+       List<string> toResolve;
+       Dictionary<string, int> locals;
+       bool jumpToLast;
+
+       internal SType StackEffect {
+               get; set;
+       }
+
+       /*
+        * Create a new instance, with the specified word name.
+        */
+       internal WordBuilder(T0Comp TC, string name)
+       {
+               this.TC = TC;
+               this.name = name;
+               cfStack = new int[16];
+               cfPtr = -1;
+               code = new List<Opcode>();
+               toResolve = new List<string>();
+               locals = new Dictionary<string, int>();
+               jumpToLast = true;
+               StackEffect = SType.UNKNOWN;
+       }
+
+       /*
+        * Build the word. The control-flow stack must be empty. A 'ret'
+        * opcode is automatically appended if required.
+        */
+       internal Word Build()
+       {
+               if (cfPtr != -1) {
+                       throw new Exception("control-flow stack is not empty");
+               }
+               if (jumpToLast || code[code.Count - 1].MayFallThrough) {
+                       Ret();
+               }
+               Word w = new WordInterpreted(TC, name, locals.Count,
+                       code.ToArray(), toResolve.ToArray());
+               w.StackEffect = StackEffect;
+               return w;
+       }
+
+       void Add(Opcode op)
+       {
+               Add(op, null);
+       }
+
+       void Add(Opcode op, string refName)
+       {
+               code.Add(op);
+               toResolve.Add(refName);
+               jumpToLast = false;
+       }
+
+       /*
+        * Rotate the control-flow stack at depth 'depth'.
+        */
+       internal void CSRoll(int depth)
+       {
+               int x = cfStack[cfPtr - depth];
+               Array.Copy(cfStack, cfPtr - (depth - 1),
+                       cfStack, cfPtr - depth, depth);
+               cfStack[cfPtr] = x;
+       }
+
+       /*
+        * Make a copy of the control-flow element at depth 'depth', and
+        * push it on top of the control-flow stack.
+        */
+       internal void CSPick(int depth)
+       {
+               int x = cfStack[cfPtr - depth];
+               CSPush(x);
+       }
+
+       void CSPush(int x)
+       {
+               int len = cfStack.Length;
+               if (++ cfPtr == len) {
+                       int[] ncf = new int[len << 1];
+                       Array.Copy(cfStack, 0, ncf, 0, len);
+                       cfStack = ncf;
+               }
+               cfStack[cfPtr] = x;
+       }
+
+       int CSPop()
+       {
+               return cfStack[cfPtr --];
+       }
+
+       /*
+        * Push an origin on the control-flow stack, corresponding to the
+        * next opcode to add.
+        */
+       internal void CSPushOrig()
+       {
+               CSPush(code.Count);
+       }
+
+       /*
+        * Push a destination on the control-flow stack, corresponding to
+        * the next opcode to add.
+        */
+       internal void CSPushDest()
+       {
+               CSPush(-code.Count - 1);
+       }
+
+       /*
+        * Pop an origin from the control-flow stack. An exception is
+        * thrown if the value is not an origin.
+        */
+       internal int CSPopOrig()
+       {
+               int x = CSPop();
+               if (x < 0) {
+                       throw new Exception("not an origin");
+               }
+               return x;
+       }
+
+       /*
+        * Pop a destination from the control-flow stack. An exception is
+        * thrown if the value is not a destination.
+        */
+       internal int CSPopDest()
+       {
+               int x = CSPop();
+               if (x >= 0) {
+                       throw new Exception("not a destination");
+               }
+               return -x - 1;
+       }
+
+       /*
+        * Add a "push literal" opcode.
+        */
+       internal void Literal(TValue v)
+       {
+               Add(new OpcodeConst(v));
+       }
+
+       /*
+        * Compile a "call" by name. This method implements the support
+        * for local variables:
+        *
+        *  - If the target is '>' followed by a local variable name, then
+        *    a "put local" opcode is added.
+        *
+        *  - Otherwise, if the target is a local variable name, then a
+        *    "get local" opcode is added.
+        *
+        *  - Otherwise, a call to the named word is added. The target name
+        *    will be resolved later on (typically, when the word containing
+        *    the call opcode is first invoked, or when C code is generated).
+        */
+       internal void Call(string target)
+       {
+               string lname;
+               bool write;
+               if (target.StartsWith(">")) {
+                       lname = target.Substring(1);
+                       write = true;
+               } else {
+                       lname = target;
+                       write = false;
+               }
+               int lnum;
+               if (locals.TryGetValue(lname, out lnum)) {
+                       if (write) {
+                               Add(new OpcodePutLocal(lnum));
+                       } else {
+                               Add(new OpcodeGetLocal(lnum));
+                       }
+               } else {
+                       Add(new OpcodeCall(), target);
+               }
+       }
+
+       /*
+        * Add a "call" opcode to the designated word.
+        */
+       internal void CallExt(Word wtarget)
+       {
+               Add(new OpcodeCall(wtarget), null);
+       }
+
+       /*
+        * Add a "call" opcode to a word which is not currently resolved.
+        * This method ignores local variables.
+        */
+       internal void CallExt(string target)
+       {
+               Add(new OpcodeCall(), target);
+       }
+
+       /*
+        * Add a "get local" opcode; the provided local name must already
+        * be defined.
+        */
+       internal void GetLocal(string name)
+       {
+               int lnum;
+               if (locals.TryGetValue(name, out lnum)) {
+                       Add(new OpcodeGetLocal(lnum));
+               } else {
+                       throw new Exception("no such local: " + name);
+               }
+       }
+
+       /*
+        * Add a "put local" opcode; the provided local name must already
+        * be defined.
+        */
+       internal void PutLocal(string name)
+       {
+               int lnum;
+               if (locals.TryGetValue(name, out lnum)) {
+                       Add(new OpcodePutLocal(lnum));
+               } else {
+                       throw new Exception("no such local: " + name);
+               }
+       }
+
+       /*
+        * Define a new local name.
+        */
+       internal void DefLocal(string lname)
+       {
+               if (locals.ContainsKey(lname)) {
+                       throw new Exception(String.Format(
+                               "local already defined: {0}", lname));
+               }
+               locals[lname] = locals.Count;
+       }
+
+       /*
+        * Add a "call" opcode whose target is an XT value (which may be
+        * resolved or as yet unresolved).
+        */
+       internal void Call(TPointerXT xt)
+       {
+               if (xt.Target == null) {
+                       Add(new OpcodeCall(), xt.Name);
+               } else {
+                       Add(new OpcodeCall(xt.Target));
+               }
+       }
+
+       /*
+        * Add a "ret" opcode.
+        */
+       internal void Ret()
+       {
+               Add(new OpcodeRet());
+       }
+
+       /*
+        * Add a forward unconditional jump. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void Ahead()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpUncond());
+       }
+
+       /*
+        * Add a forward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'true'. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void AheadIf()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpIf());
+       }
+
+       /*
+        * Add a forward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'false'. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void AheadIfNot()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpIfNot());
+       }
+
+       /*
+        * Resolve a previous forward jump to the current code address.
+        * The top of control-flow stack is popped and must be an origin.
+        */
+       internal void Then()
+       {
+               int x = CSPopOrig();
+               code[x].ResolveJump(code.Count - x - 1);
+               jumpToLast = true;
+       }
+
+       /*
+        * Push the current code address on the control-flow stack as a
+        * destination, to be used by an ulterior backward jump.
+        */
+       internal void Begin()
+       {
+               CSPushDest();
+       }
+
+       /*
+        * Add a backward unconditional jump. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void Again()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpUncond(x - code.Count - 1));
+       }
+
+       /*
+        * Add a backward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'true'. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void AgainIf()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpIf(x - code.Count - 1));
+       }
+
+       /*
+        * Add a backward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'false'. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void AgainIfNot()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpIfNot(x - code.Count - 1));
+       }
+}
diff --git a/T0/WordData.cs b/T0/WordData.cs
new file mode 100644 (file)
index 0000000..2d74bd3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class WordData : Word {
+
+       ConstData blob;
+       string baseBlobName;
+       int offset;
+       bool ongoingResolution;
+
+       internal WordData(T0Comp owner, string name,
+               ConstData blob, int offset)
+               : base(owner, name)
+       {
+               this.blob = blob;
+               this.offset = offset;
+               StackEffect = new SType(0, 1);
+       }
+
+       internal WordData(T0Comp owner, string name,
+               string baseBlobName, int offset)
+               : base(owner, name)
+       {
+               this.baseBlobName = baseBlobName;
+               this.offset = offset;
+               StackEffect = new SType(0, 1);
+       }
+
+       internal override void Resolve()
+       {
+               if (blob != null) {
+                       return;
+               }
+               if (ongoingResolution) {
+                       throw new Exception(String.Format(
+                               "circular reference in blobs ({0})", Name));
+               }
+               ongoingResolution = true;
+               WordData wd = TC.Lookup(baseBlobName) as WordData;
+               if (wd == null) {
+                       throw new Exception(String.Format(
+                               "data word '{0}' based on non-data word '{1}'",
+                               Name, baseBlobName));
+               }
+               wd.Resolve();
+               blob = wd.blob;
+               offset += wd.offset;
+               ongoingResolution = false;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               Resolve();
+               cpu.Push(new TValue(offset, new TPointerBlob(blob)));
+       }
+
+       internal override List<ConstData> GetDataBlocks()
+       {
+               Resolve();
+               List<ConstData> r = new List<ConstData>();
+               r.Add(blob);
+               return r;
+       }
+
+       internal override void GenerateCodeElements(List<CodeElement> dst)
+       {
+               Resolve();
+               dst.Add(new CodeElementUInt(0));
+               dst.Add(new CodeElementUIntInt(1, blob.Address + offset));
+               dst.Add(new CodeElementUInt(0));
+       }
+}
diff --git a/T0/WordInterpreted.cs b/T0/WordInterpreted.cs
new file mode 100644 (file)
index 0000000..882170b
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * The implementation for interpreted words.
+ */
+
+class WordInterpreted : Word {
+
+       /*
+        * Get the number of local variables for this word.
+        */
+       internal int NumLocals {
+               get; private set;
+       }
+
+       /*
+        * Get the sequence of opcodes for this word.
+        */
+       internal Opcode[] Code {
+               get; private set;
+       }
+
+       string[] toResolve;
+
+       internal WordInterpreted(T0Comp owner, string name,
+               int numLocals, Opcode[] code, string[] toResolve)
+               : base(owner, name)
+       {
+               this.Code = code;
+               this.toResolve = toResolve;
+               NumLocals = numLocals;
+       }
+
+       internal override void Resolve()
+       {
+               if (toResolve == null) {
+                       return;
+               }
+               for (int i = 0; i < toResolve.Length; i ++) {
+                       string tt = toResolve[i];
+                       if (tt == null) {
+                               continue;
+                       }
+                       Code[i].ResolveTarget(TC.Lookup(tt));
+               }
+               toResolve = null;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               Resolve();
+               cpu.Enter(Code, NumLocals);
+       }
+
+       internal override List<Word> GetReferences()
+       {
+               Resolve();
+               List<Word> r = new List<Word>();
+               foreach (Opcode op in Code) {
+                       Word w = op.GetReference(TC);
+                       if (w != null) {
+                               r.Add(w);
+                       }
+               }
+               return r;
+       }
+
+       internal override List<ConstData> GetDataBlocks()
+       {
+               Resolve();
+               List<ConstData> r = new List<ConstData>();
+               foreach (Opcode op in Code) {
+                       ConstData cd = op.GetDataBlock(TC);
+                       if (cd != null) {
+                               r.Add(cd);
+                       }
+               }
+               return r;
+       }
+
+       internal override void GenerateCodeElements(List<CodeElement> dst)
+       {
+               Resolve();
+               int n = Code.Length;
+               CodeElement[] gcode = new CodeElement[n];
+               for (int i = 0; i < n; i ++) {
+                       gcode[i] = Code[i].ToCodeElement();
+               }
+               for (int i = 0; i < n; i ++) {
+                       Code[i].FixUp(gcode, i);
+               }
+               dst.Add(new CodeElementUInt((uint)NumLocals));
+               for (int i = 0; i < n; i ++) {
+                       dst.Add(gcode[i]);
+               }
+       }
+
+       int flowAnalysis;
+       int maxDataStack;
+       int maxReturnStack;
+
+       bool MergeSA(int[] sa, int j, int c)
+       {
+               if (sa[j] == Int32.MinValue) {
+                       sa[j] = c;
+                       return true;
+               } else if (sa[j] != c) {
+                       throw new Exception(string.Format(
+                               "In word '{0}', offset {1}:"
+                               + " stack action mismatch ({2} / {3})",
+                               Name, j, sa[j], c));
+               } else {
+                       return false;
+               }
+       }
+
+       internal override void AnalyseFlow()
+       {
+               switch (flowAnalysis) {
+               case 0:
+                       break;
+               case 1:
+                       return;
+               default:
+                       throw new Exception("recursive call detected in '"
+                               + Name + "'");
+               }
+               flowAnalysis = 2;
+               int n = Code.Length;
+               int[] sa = new int[n];
+               for (int i = 0; i < n; i ++) {
+                       sa[i] = Int32.MinValue;
+               }
+               sa[0] = 0;
+               int[] toExplore = new int[n];
+               int tX = 0, tY = 0;
+               int off = 0;
+
+               int exitSA = Int32.MinValue;
+               int mds = 0;
+               int mrs = 0;
+
+               int maxDepth = 0;
+
+               for (;;) {
+                       Opcode op = Code[off];
+                       bool mft = op.MayFallThrough;
+                       int c = sa[off];
+                       int a;
+                       if (op is OpcodeCall) {
+                               Word w = op.GetReference(TC);
+                               w.AnalyseFlow();
+                               SType se = w.StackEffect;
+                               if (!se.IsKnown) {
+                                       throw new Exception(string.Format(
+                                               "call from '{0}' to '{1}'"
+                                               + " with unknown stack effect",
+                                               Name, w.Name));
+                               }
+                               if (se.NoExit) {
+                                       mft = false;
+                                       a = 0;
+                               } else {
+                                       a = se.DataOut - se.DataIn;
+                               }
+                               mds = Math.Max(mds, c + w.MaxDataStack);
+                               mrs = Math.Max(mrs, w.MaxReturnStack);
+                               maxDepth = Math.Min(maxDepth, c - se.DataIn);
+                       } else if (op is OpcodeRet) {
+                               if (exitSA == Int32.MinValue) {
+                                       exitSA = c;
+                               } else if (exitSA != c) {
+                                       throw new Exception(string.Format(
+                                               "'{0}': exit stack action"
+                                               + " mismatch: {1} / {2}"
+                                               + " (offset {3})",
+                                               Name, exitSA, c, off));
+                               }
+                               a = 0;
+                       } else {
+                               a = op.StackAction;
+                               mds = Math.Max(mds, c + a);
+                       }
+                       c += a;
+                       maxDepth = Math.Min(maxDepth, c);
+
+                       int j = op.JumpDisp;
+                       if (j != 0) {
+                               j += off + 1;
+                               toExplore[tY ++] = j;
+                               MergeSA(sa, j, c);
+                       }
+                       off ++;
+                       if (!mft || !MergeSA(sa, off, c)) {
+                               if (tX < tY) {
+                                       off = toExplore[tX ++];
+                               } else {
+                                       break;
+                               }
+                       }
+               }
+
+               maxDataStack = mds;
+               maxReturnStack = 1 + NumLocals + mrs;
+
+               /*
+                * TODO: see about this warning. Usage of a 'fail'
+                * word (that does not exit) within a 'case..endcase'
+                * structure will make an unreachable opcode. In a future
+                * version we might want to automatically remove dead
+                * opcodes.
+               for (int i = 0; i < n; i ++) {
+                       if (sa[i] == Int32.MinValue) {
+                               Console.WriteLine("warning: word '{0}',"
+                                       + " offset {1}: unreachable opcode",
+                                       Name, i);
+                               continue;
+                       }
+               }
+                */
+
+               SType computed;
+               if (exitSA == Int32.MinValue) {
+                       computed = new SType(-maxDepth, -1);
+               } else {
+                       computed = new SType(-maxDepth, -maxDepth + exitSA);
+               }
+
+               if (StackEffect.IsKnown) {
+                       if (!computed.IsSubOf(StackEffect)) {
+                               throw new Exception(string.Format(
+                                       "word '{0}':"
+                                       + " computed stack effect {1}"
+                                       + " does not match declared {2}",
+                                       Name, computed.ToString(),
+                                       StackEffect.ToString()));
+                       }
+               } else {
+                       StackEffect = computed;
+               }
+
+               flowAnalysis = 1;
+       }
+
+       internal override int MaxDataStack {
+               get {
+                       AnalyseFlow();
+                       return maxDataStack;
+               }
+       }
+
+       internal override int MaxReturnStack {
+               get {
+                       AnalyseFlow();
+                       return maxReturnStack;
+               }
+       }
+}
diff --git a/T0/WordNative.cs b/T0/WordNative.cs
new file mode 100644 (file)
index 0000000..7868872
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * th