+<AND? <SETG LICENSE "
+Copyright (C) 2017-2018 Keziah Wesley
+
+You can redistribute and/or modify this file under the terms of the
+GNU Affero General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public
+License along with this file. If not, see
+<http://www.gnu.org/licenses/>.
+">
+
+<DEFINE result (expr) <STRING "*_res = (" .expr ");">>
+
+<SETG opmname 1> ;"assembler name (e.g. GRTR?, SUB)"
+<SETG opcode 2> ;"numeric opcode"
+<SETG opargs 3> ;"argspec. see examples."
+<SETG opbody-c 4> ;"C implementation (use names from argspec)"
+<SETG opcname 5> ;"unique name (e.g. GRTRB, SUBB)"
+
+<DEFINE def-ops ("TUPLE" ops "AUX" (i 0) (results '()) "NAME" act)
+ <COND (<EMPTY? .ops> <RETURN .results .act>)>
+ <SET results <CONS (<1 <2 .ops>> .i <REST <2 .ops>> <3 .ops> <UNPARSE <1 .ops>>) .results>>
+ <SET ops <REST <REST <REST .ops>>>>
+ <SET i <+ .i 1>>
+ <AGAIN .act>>
+
+<DEFINE def-name-to-opcode (ops "AUX" (rslt "enum {") "NAME" act)
+ <COND (<EMPTY? .ops> <RETURN <STRING .rslt "};"> .act>)>
+ <SET rslt <STRING .rslt "I_" <opcname <1 .ops>> "=" <UNPARSE <opcode <1 .ops>>> ",">>
+ <SET ops <REST .ops>>
+ <AGAIN .act>>
+
+<DEFINE def-opcode-to-name (ops "AUX" (rslt "const char * opcode_to_name (int op) { switch (op) {") "NAME" act)
+ <COND (<EMPTY? .ops> <RETURN <STRING .rslt "default: return 0;}}"> .act>)>
+ <SET rslt <STRING .rslt "case " <UNPARSE <opcode <1 .ops>>> ": return \"" <opcname <1 .ops>> "\";" ,CR>>
+ <SET ops <REST .ops>>
+ <AGAIN .act>>
+
+<DEFINE read-args (args "AUX" (acc "") (i 1) "NAME" act)
+ <COND (<EMPTY? .args> <RETURN .acc .act>)>
+ <SET acc <STRING .acc
+ <COND
+ (<==? <1 .args> result>
+ <STRING "object * _res = RESULT(" <UNPARSE .i> ");" ,CR>)
+ (ELSE
+ <COND
+ (<==? <2 <1 .args>> BYTE>
+ <STRING "uint8_t " <UNPARSE <1 <1 .args>>> " = p[" <UNPARSE .i> "];" ,CR>)
+ (<==? <2 <1 .args>> OFFSET>
+ <STRING "int8_t " <UNPARSE <1 <1 .args>>> " = (int8_t)p[" <UNPARSE .i> "];" ,CR>)
+ (<==? <2 <1 .args>> FIX>
+ <STRING "int64_t " <UNPARSE <1 <1 .args>>> " = GETFIX(bp[p[" <UNPARSE .i> "]]);" ,CR>)
+ >)>>>
+ <SET args <REST .args>>
+ <SET i <+ .i 1>>
+ <AGAIN .act>>
+
+<DEFINE def-dispatch (ops
+ "AUX" (rslt <STRING "void run (const uint8_t * p0, const uint8_t * p, object * bp, int sp) {" ,CR
+ "for (;;) {" ,CR
+ "TRACE(p0, p);" ,CR
+ "switch (*p) {" ,CR>)
+ "NAME" act)
+ <COND (<EMPTY? .ops> <RETURN
+ <STRING .rslt "default: assert(0 && \"bad opcode\");" ,CR "}" ,CR "}" ,CR "}" ,CR> .act>)>
+ <SET rslt
+ <STRING .rslt "case I_" <opcname <1 .ops>> ": {" ,CR
+ <read-args <opargs <1 .ops>>>
+ "const uint8_t * const oldp = p;" ,CR
+ "p += " <UNPARSE <+ 1 <LENGTH <opargs <1 .ops>>>>> !\; ,CR
+ <opbody-c <1 .ops>> ,CR
+ "break;" ,CR !\} ,CR>>
+ <SET ops <REST .ops>>
+ <AGAIN .act>>
+
+<SETG CR !\
+>
+
+<DEFINE gen-opcode-fmt (op "AUX" (args <opargs .op>) (s "\"<%s") "NAME" act)
+ <COND (<EMPTY? .args> <RETURN <STRING .s ">\\n\""> .act>)>
+ <SET s
+ <STRING .s " "
+ <COND
+ (<==? <1 .args> result>
+ "%hhi")
+ (ELSE <COND
+ (<==? <2 <1 .args>> BYTE>
+ "%hhu")
+ (<==? <2 <1 .args>> OFFSET>
+ "%hhi")
+ (<==? <2 <1 .args>> FIX>
+ "fix%hhu")>)>>>
+ <SET args <REST .args>>
+ <AGAIN .act>>
+
+<DEFINE gen-opcode-args (op base "AUX"
+ (s <STRING ", \"" <opcname .op> "\"">)
+ (i 0)
+ (n <LENGTH <opargs .op>>))
+ <PROG ("NAME" act)
+ <COND (<==? .i .n> <RETURN .s .act>)>
+ <SET i <+ 1 .i>>
+ <SET s <STRING .s ", " .base "[" <UNPARSE .i> "]">>
+ <AGAIN .act>>>
+
+<DEFINE def-disasm (ops
+ "AUX" (rslt <STRING "int snprint_op_disasm"
+ "(char ** buf, size_t outlen, const uint8_t ** p, size_t inlen)" ,CR
+ "{" ,CR "switch (**p) {" ,CR>)
+ "NAME" act)
+ <COND (<EMPTY? .ops> <RETURN <STRING .rslt "}" ,CR "return EBADOPCODE;" ,CR "}"> .act>)>
+ <SET rslt
+ <STRING .rslt "case " <UNPARSE <opcode <1 .ops>>> ": " ,CR
+ "if (inlen < " <UNPARSE <LENGTH <opargs <1 .ops>>>> ") { return EBADOPARGS; };" ,CR
+ "*buf += snprintf(*buf, outlen, "
+ <gen-opcode-fmt <1 .ops>>
+ <gen-opcode-args <1 .ops> "(*p)">
+ ");" ,CR
+ "*p += " <UNPARSE <LENGTH <opargs <1 .ops>>>> ";" ,CR
+ "return 0;" ,CR>>
+ <SET ops <REST .ops>>
+ <AGAIN .act>>
+
+<SETG ops <def-ops
+ GRTRB (GRTR? (x FIX) (rhs BYTE) (invert BYTE) (offs OFFSET))
+ "if (invert ^ (x > rhs)) JUMP(offs);"
+ SUB (SUB (x FIX) (y FIX) result) <result "NEWFIX( x - y )">
+ SUBB (SUB (x FIX) (rhs BYTE) result) <result "NEWFIX( x - rhs )">
+ MUL (MUL (x FIX) (y FIX) result) <result "NEWFIX( x * y )">
+ HALT (HALT) "return;">>
+
+<PRINC <STRING "/*" ,LICENSE "*/">> <CRLF>
+
+<PRINC <STRING "/* GENERATED FILE. CHANGES WILL BE OVERRIDDEN. " ,CR
+ "This file is generated by Muddle code; it is included "
+ "in the distribution for bootstrapping. */">> <CRLF>
+
+<PRINC <def-name-to-opcode ,ops>> <CRLF>
+;<PRINC <def-opcode-to-name ,ops>> <CRLF>
+
+<PRINC <def-dispatch ,ops>> <CRLF>
+
+<PRINC <def-disasm ,ops>> <CRLF>
+
+;<PRINC <def-asm ,ops>> <CRLF>
+
+<QUIT>>