local generate_labels = false local replacements = { op_and16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d & b); push8(u->src, c & a); }', op_ora16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d | b); push8(u->src, c | a); }', op_eor16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d ^ b); push8(u->src, c ^ a); }', op_lit16 = '{ push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); }', op_swp16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }', op_ovr16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }', op_dup16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, b); push8(u->src, a); }', op_rot16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src), e = pop8(u->src), f = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, f); push8(u->src, e); }', op_sth16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->dst, b); push8(u->dst, a); }' } local top, bottom, pushtop local offset offset = function(n, s) if s == nil then s = '' end if n < 0 then return (' -%s %d'):format(s, -n) elseif n > 0 then return (' +%s %d'):format(s, n) elseif s ~= '' then return (' +%s 0'):format(s) else return '' end end local pop_push pop_push = function(k, n, s) local _exp_0 = k if 'pop' == _exp_0 then s = s:match('^%((%S+)%)$') assert(s == 'src') local _exp_1 = n if '8' == _exp_1 then top[s] = top[s] - 1 if bottom[s] > top[s] then bottom[s] = top[s] end return ('%s.dat[%s.ptr%s]'):format(s, s, offset(top[s])) elseif '16' == _exp_1 then top[s] = top[s] - 2 if bottom[s] > top[s] then bottom[s] = top[s] end return ('(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'):format(s, s, offset(top[s] + 1), s, s, offset(top[s])) end elseif 'push' == _exp_0 then local v s, v = s:match('^%((%S+), (.*)%)$') assert(s == 'src' or s == 'dst', s) local _exp_1 = n if '8' == _exp_1 then pushtop[s] = pushtop[s] + 1 return ('%s.dat[%s.ptr%s] = %s'):format(s, s, offset(pushtop[s] - 1), v) elseif '16' == _exp_1 then if v:match('%+%+') or v:match('%-%-') then error('push16 has side effects: ' .. v) end local peek, args = v:match('^([md]e[mv]peek)16(%b())$') if peek then args = args:sub(2, -2) return pop_push('push', '8', ('(%s, %s8(%s))'):format(s, peek, args)) .. ';\n' .. pop_push('push', '8', ('(%s, %s8(%s + 1))'):format(s, peek, args)) end pushtop[s] = pushtop[s] + 2 if v:match(' ') then v = '(' .. v .. ')' end return ('%s.dat[%s.ptr%s] = %s >> 8;\n%s.dat[%s.ptr%s] = %s & 0xff'):format(s, s, offset(pushtop[s] - 2), v, s, s, offset(pushtop[s] - 1), v) end else return nil end end local indented_block indented_block = function(s) s = s:gsub('^%{ *', '{\n'):gsub('\n', '\n\t'):gsub('\t%} *$', '}\n') s = s:gsub('\n[^\n]+%.error = [^\n]+', '%0\n#ifndef NO_STACK_CHECKS\n\tgoto error;\n#endif') return s end local process process = function(body) local out_body = body:gsub('^%{ *', ''):gsub(' *%}$', ''):gsub('; ', ';\n'):gsub('%b{} *', indented_block):gsub('(%a+)(%d+)(%b())', pop_push) local in_ifdef = false local _list_0 = { 'src', 'dst' } for _index_0 = 1, #_list_0 do local k = _list_0[_index_0] if bottom[k] ~= 0 then if not in_ifdef then out_body = out_body .. '\n#ifndef NO_STACK_CHECKS' in_ifdef = true end out_body = out_body .. ('\nif(__builtin_expect(%s.ptr < %d, 0)) {\n\t%s.error = 1;\n\tgoto error;\n}'):format(k, -bottom[k], k) end if pushtop[k] ~= 0 then if pushtop[k] > 0 then if not in_ifdef then out_body = out_body .. '\n#ifndef NO_STACK_CHECKS' in_ifdef = true end out_body = out_body .. ('\nif(__builtin_expect(%s.ptr > %d, 0)) {\n\t%s.error = 2;\n\tgoto error;\n}'):format(k, 255 - pushtop[k], k) end if in_ifdef then out_body = out_body .. '\n#endif' in_ifdef = false end out_body = out_body .. ('\n%s.ptr %s= %d;'):format(k, pushtop[k] < 0 and '-' or '+', math.abs(pushtop[k])) end end if in_ifdef then out_body = out_body .. '\n#endif' in_ifdef = false end local t = { } out_body:gsub('[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', function(v, k) t[k] = v end) out_body = out_body:gsub('(src%.dat%[[^]]+%]) = ([a-f]);\n', function(k, v) if t[k] and t[k] == v then return '' end return nil end) return out_body end local ops = { } for l in assert(io.lines('src/uxn.c')) do local _continue_0 = false repeat local name, body = l:match('void (op_%S*)%(Uxn %*u%) (%b{})') if not name then _continue_0 = true break end if replacements[name] then body = replacements[name] end body = body:gsub('u%-%>src%-%>', 'src.') body = body:gsub('u%-%>dst%-%>', 'dst.') body = body:gsub('u%-%>src', 'src') body = body:gsub('u%-%>dst', 'dst') top = { src = 0, dst = 0 } bottom = { src = 0, dst = 0 } pushtop = top ops[name] = process(body) top = { src = 0, dst = 0 } bottom = { src = 0, dst = 0 } pushtop = { src = 0, dst = 0 } ops['keep_' .. name] = process(body) _continue_0 = true until true if not _continue_0 then break end end local dump dump = function(s, src, dst) local ret = '\t\t\t{\n' for l in s:gmatch('[^\n]+') do if not l:match('^%#') then ret = ret .. '\t\t\t\t' end ret = ret .. ('%s\n'):format(l) end ret = ret .. '\t\t\t}\n\t\t\tbreak;\n' return (ret:gsub('src', src):gsub('dst', dst)) end local i = 0 local allops = { } local wanted = false for l in assert(io.lines('src/uxn.c')) do if l == 'static void (*ops[])(Uxn *u) = {' then wanted = true elseif l == '};' then wanted = false elseif wanted then l = l:gsub('%/%b**%/', '') for op in l:gmatch('[%w_]+') do if not ops[op] then error('missing ' .. op) end allops[i + 0x00 + 1] = { n = { i + 0x00 }, body = dump(ops[op], 'u->wst', 'u->rst') } allops[i + 0x20 + 1] = { n = { i + 0x20 }, body = dump(ops[op], 'u->rst', 'u->wst') } allops[i + 0x80 + 1] = { n = { i + 0x80 }, body = dump(ops['keep_' .. op], 'u->wst', 'u->rst') } allops[i + 0xa0 + 1] = { n = { i + 0xa0 }, body = dump(ops['keep_' .. op], 'u->rst', 'u->wst') } i = i + 1 if i == 0x20 then i = i + 0x20 end end end end i = 0 wanted = false for l in assert(io.lines('src/uxnasm.c')) do if l == 'static char ops[][4] = {' then wanted = true elseif l == '};' then wanted = false elseif wanted then for op in l:gmatch('"(...)"') do i = i + 1 allops[i + 0x00].name = op allops[i + 0x20].name = op .. 'r' allops[i + 0x40].name = op .. '2' allops[i + 0x60].name = op .. '2r' allops[i + 0x80].name = op .. 'k' allops[i + 0xa0].name = op .. 'kr' allops[i + 0xc0].name = op .. '2k' allops[i + 0xe0].name = op .. '2kr' end end end for i = 1, 256 do local _continue_0 = false repeat if not allops[i] then _continue_0 = true break end for j = i + 1, 256 do if allops[i].body == allops[j].body then table.insert(allops[i].n, (table.remove(allops[j].n))) allops[j].body = nil end end _continue_0 = true until true if not _continue_0 then break end end do local _with_0 = assert(io.open('src/uxn-fast.c', 'w')) local f = assert(io.open('src/uxn.c')) while true do local l = f:read('*l') _with_0:write(('%s\n'):format(l)) if l == '*/' then break end end _with_0:write('\n') _with_0:write([[/* ^ /!\ THIS FILE IS AUTOMATICALLY GENERATED --- Its contents can get overwritten with the processed contents of src/uxn.c. See etc/mkuxn-fast.moon for instructions. */ ]]) wanted = true while true do local _continue_0 = false repeat local l = f:read('*l') if l:match(' push') or l:match('[ *]pop') or l:match('devr16') then _continue_0 = true break end if l == '/* Stack */' then wanted = false end if wanted then _with_0:write(('%s\n'):format(l)) end if l == '}' then _with_0:write('\n') break end _continue_0 = true until true if not _continue_0 then break end end _with_0:write([[/* clang-format on */ #pragma mark - Core int uxn_eval(Uxn *u, Uint16 vec) { Uint8 instr; if(!vec || u->dev[0].dat[0xf]) return 0; u->ram.ptr = vec; if(u->wst.ptr > 0xf8) u->wst.ptr = 0xf8; while((instr = u->ram.dat[u->ram.ptr++])) { switch(instr) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-value" #pragma GCC diagnostic ignored "-Wunused-variable" ]]) for i = 1, 256 do local _continue_0 = false repeat if not allops[i].body then _continue_0 = true break end local _list_0 = allops[i].n for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] _with_0:write(('\t\tcase 0x%02x: /* %s */\n'):format(n, allops[n + 1].name)) end if generate_labels then _with_0:write(('\t\t\t__asm__("evaluxn_%02x_%s:");\n'):format(allops[i].n[1], allops[i].name)) end _with_0:write(allops[i].body) _continue_0 = true until true if not _continue_0 then break end end _with_0:write([[#pragma GCC diagnostic pop } } return 1; #ifndef NO_STACK_CHECKS error: if(u->wst.error) return uxn_halt(u, u->wst.error, "Working-stack", instr); else return uxn_halt(u, u->rst.error, "Return-stack", instr); #endif } int ]]) wanted = false while true do local l = f:read('*l') if not l then break end if l:match('^uxn_boot') then wanted = true end if wanted then _with_0:write(('%s\n'):format(l)) end end f:close() _with_0:close() return _with_0 end