mirror of https://git.sr.ht/~rabbits/uxn
30 changed files with 1046 additions and 5179 deletions
@ -1,389 +0,0 @@ |
|||||||
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 |
|
||||||
@ -1,280 +0,0 @@ |
|||||||
-- |
|
||||||
-- Uxn core unroller script |
|
||||||
-- |
|
||||||
-- This script updates src/uxn-fast.c when Uxn's opcode set changes, so that |
|
||||||
-- updates in the human-readable src/uxn.c core can be easily converted into |
|
||||||
-- high-performance code. |
|
||||||
-- |
|
||||||
-- To run, you need Lua or LuaJIT, and just run etc/mkuxn-fast.lua from the top |
|
||||||
-- directory of Uxn's git repository: |
|
||||||
-- |
|
||||||
-- lua etc/mkuxn-fast.lua |
|
||||||
-- |
|
||||||
-- This file is written in MoonScript, which is a language that compiles to |
|
||||||
-- Lua, the same way as e.g. CoffeeScript compiles to JavaScript. Since |
|
||||||
-- installing MoonScript has more dependencies than Lua, the compiled |
|
||||||
-- etc/mkuxn-fast.lua is kept in Uxn's repository and will be kept updated as |
|
||||||
-- this file changes. |
|
||||||
-- |
|
||||||
|
|
||||||
generate_labels = false -- add labels to each opcode to inspect disassembled code |
|
||||||
|
|
||||||
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 |
|
||||||
|
|
||||||
offset = (n, s = '') -> |
|
||||||
if n < 0 |
|
||||||
' -%s %d'\format s, -n |
|
||||||
elseif n > 0 |
|
||||||
' +%s %d'\format s, n |
|
||||||
elseif s != '' |
|
||||||
' +%s 0'\format s |
|
||||||
else |
|
||||||
'' |
|
||||||
|
|
||||||
pop_push = (k, n, s) -> |
|
||||||
switch k |
|
||||||
when 'pop' |
|
||||||
s = s\match '^%((%S+)%)$' |
|
||||||
assert s == 'src' |
|
||||||
switch n |
|
||||||
when '8' |
|
||||||
top[s] -= 1 |
|
||||||
if bottom[s] > top[s] |
|
||||||
bottom[s] = top[s] |
|
||||||
'%s.dat[%s.ptr%s]'\format s, s, offset(top[s]) |
|
||||||
when '16' |
|
||||||
top[s] -= 2 |
|
||||||
if bottom[s] > top[s] |
|
||||||
bottom[s] = top[s] |
|
||||||
'(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'\format s, s, offset(top[s] + 1), s, s, offset(top[s]) |
|
||||||
when 'push' |
|
||||||
s, v = s\match '^%((%S+), (.*)%)$' |
|
||||||
assert s == 'src' or s == 'dst', s |
|
||||||
switch n |
|
||||||
when '8' |
|
||||||
pushtop[s] += 1 |
|
||||||
'%s.dat[%s.ptr%s] = %s'\format s, s, offset(pushtop[s] - 1), v |
|
||||||
when '16' |
|
||||||
if v\match'%+%+' or v\match'%-%-' |
|
||||||
error 'push16 has side effects: ' .. v |
|
||||||
peek, args = v\match '^([md]e[mv]peek)16(%b())$' |
|
||||||
if peek |
|
||||||
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) |
|
||||||
pushtop[s] += 2 |
|
||||||
if v\match ' ' |
|
||||||
v = '(' .. v .. ')' |
|
||||||
'%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 |
|
||||||
else |
|
||||||
nil |
|
||||||
|
|
||||||
indented_block = (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') |
|
||||||
s |
|
||||||
|
|
||||||
process = (body) -> |
|
||||||
out_body = body\gsub('^%{ *', '')\gsub(' *%}$', '')\gsub('; ', ';\n')\gsub('%b{} *', indented_block)\gsub '(%a+)(%d+)(%b())', pop_push |
|
||||||
in_ifdef = false |
|
||||||
for k in *{'src', 'dst'} |
|
||||||
if bottom[k] != 0 |
|
||||||
if not in_ifdef |
|
||||||
out_body ..= '\n#ifndef NO_STACK_CHECKS' |
|
||||||
in_ifdef = true |
|
||||||
out_body ..= '\nif(__builtin_expect(%s.ptr < %d, 0)) {\n\t%s.error = 1;\n\tgoto error;\n}'\format k, -bottom[k], k |
|
||||||
if pushtop[k] != 0 |
|
||||||
if pushtop[k] > 0 |
|
||||||
if not in_ifdef |
|
||||||
out_body ..= '\n#ifndef NO_STACK_CHECKS' |
|
||||||
in_ifdef = true |
|
||||||
out_body ..= '\nif(__builtin_expect(%s.ptr > %d, 0)) {\n\t%s.error = 2;\n\tgoto error;\n}'\format k, 255 - pushtop[k], k |
|
||||||
if in_ifdef |
|
||||||
out_body ..= '\n#endif' |
|
||||||
in_ifdef = false |
|
||||||
out_body ..= '\n%s.ptr %s= %d;'\format k, pushtop[k] < 0 and '-' or '+', math.abs pushtop[k] |
|
||||||
if in_ifdef |
|
||||||
out_body ..= '\n#endif' |
|
||||||
in_ifdef = false |
|
||||||
t = {} |
|
||||||
out_body\gsub '[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', (v, k) -> t[k] = v |
|
||||||
out_body = out_body\gsub '(src%.dat%[[^]]+%]) = ([a-f]);\n', (k, v) -> |
|
||||||
if t[k] and t[k] == v |
|
||||||
return '' |
|
||||||
return nil |
|
||||||
out_body |
|
||||||
|
|
||||||
ops = {} |
|
||||||
|
|
||||||
for l in assert io.lines 'src/uxn.c' |
|
||||||
name, body = l\match 'void (op_%S*)%(Uxn %*u%) (%b{})' |
|
||||||
if not name |
|
||||||
continue |
|
||||||
if replacements[name] |
|
||||||
body = replacements[name] |
|
||||||
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 |
|
||||||
|
|
||||||
dump = (s, src, dst) -> |
|
||||||
ret = '\t\t\t{\n' |
|
||||||
for l in s\gmatch '[^\n]+' |
|
||||||
if not l\match '^%#' |
|
||||||
ret ..= '\t\t\t\t' |
|
||||||
ret ..= '%s\n'\format l |
|
||||||
ret ..= '\t\t\t}\n\t\t\tbreak;\n' |
|
||||||
(ret\gsub('src', src)\gsub('dst', dst)) |
|
||||||
|
|
||||||
i = 0 |
|
||||||
allops = {} |
|
||||||
wanted = false |
|
||||||
for l in assert io.lines 'src/uxn.c' |
|
||||||
if l == 'static void (*ops[])(Uxn *u) = {' |
|
||||||
wanted = true |
|
||||||
elseif l == '};' |
|
||||||
wanted = false |
|
||||||
elseif wanted |
|
||||||
l = l\gsub '%/%b**%/', '' |
|
||||||
for op in l\gmatch '[%w_]+' |
|
||||||
if not ops[op] |
|
||||||
error 'missing ' .. op |
|
||||||
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 += 1 |
|
||||||
if i == 0x20 |
|
||||||
i += 0x20 |
|
||||||
|
|
||||||
i = 0 |
|
||||||
wanted = false |
|
||||||
for l in assert io.lines 'src/uxnasm.c' |
|
||||||
if l == 'static char ops[][4] = {' |
|
||||||
wanted = true |
|
||||||
elseif l == '};' |
|
||||||
wanted = false |
|
||||||
elseif wanted |
|
||||||
for op in l\gmatch '"(...)"' |
|
||||||
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' |
|
||||||
|
|
||||||
for i = 1, 256 |
|
||||||
if not allops[i] |
|
||||||
continue |
|
||||||
for j = i + 1, 256 |
|
||||||
if allops[i].body == allops[j].body |
|
||||||
table.insert allops[i].n, (table.remove allops[j].n) |
|
||||||
allops[j].body = nil |
|
||||||
|
|
||||||
with assert io.open 'src/uxn-fast.c', 'w' |
|
||||||
f = assert io.open 'src/uxn.c' |
|
||||||
while true |
|
||||||
l = f\read '*l' |
|
||||||
\write '%s\n'\format l |
|
||||||
if l == '*/' |
|
||||||
break |
|
||||||
\write '\n' |
|
||||||
\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 |
|
||||||
l = f\read '*l' |
|
||||||
if l\match' push' or l\match'[ *]pop' or l\match'devr16' |
|
||||||
continue |
|
||||||
if l == '/* Stack */' |
|
||||||
wanted = false |
|
||||||
if wanted |
|
||||||
\write '%s\n'\format l |
|
||||||
if l == '}' |
|
||||||
\write '\n' |
|
||||||
break |
|
||||||
\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 |
|
||||||
if not allops[i].body |
|
||||||
continue |
|
||||||
for n in *allops[i].n |
|
||||||
\write '\t\tcase 0x%02x: /* %s */\n'\format n, allops[n + 1].name |
|
||||||
if generate_labels |
|
||||||
\write '\t\t\t__asm__("evaluxn_%02x_%s:");\n'\format allops[i].n[1], allops[i].name |
|
||||||
\write allops[i].body |
|
||||||
\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 |
|
||||||
l = f\read '*l' |
|
||||||
if not l |
|
||||||
break |
|
||||||
if l\match '^uxn_boot' |
|
||||||
wanted = true |
|
||||||
if wanted |
|
||||||
\write '%s\n'\format l |
|
||||||
f\close! |
|
||||||
\close! |
|
||||||
|
|
||||||
@ -0,0 +1,16 @@ |
|||||||
|
( devices ) |
||||||
|
|
||||||
|
|00 @System &vector $2 &wst $1 &rst $1 &eaddr $2 &ecode $1 &pad $1 &r $2 &g $2 &b $2 &debug $1 &halt $1 |
||||||
|
|10 @Console &vector $2 &read $1 &pad $5 &write $1 &error $1 |
||||||
|
|
||||||
|
( program ) |
||||||
|
|
||||||
|
|0100 @Reset ( -> ) |
||||||
|
~projects/library/debugger.tal |
||||||
|
#0123 #4567 #89ab #cdef |
||||||
|
LIT2r 1234 LIT2r 5678 LIT2r 9abc LIT2r def0 |
||||||
|
( BREAKPOINT ) |
||||||
|
( LIT2r 0000 DIVr ) |
||||||
|
&loop INCk ,&loop JMP |
||||||
|
BRK |
||||||
|
|
||||||
@ -0,0 +1,180 @@ |
|||||||
|
( in-Uxn debugger ) |
||||||
|
|
||||||
|
( To use, include this file just before the BRK in the program reset routine, e.g.: |
||||||
|
|
||||||
|
|0100 ( -> ) |
||||||
|
( theme ) |
||||||
|
#0fe5 .System/r DEO2 |
||||||
|
#0fc5 .System/g DEO2 |
||||||
|
#0f25 .System/b DEO2 |
||||||
|
~projects/library/debugger.tal |
||||||
|
BRK |
||||||
|
|
||||||
|
The debugger will catch stack errors that arise after that point. |
||||||
|
|
||||||
|
) |
||||||
|
|
||||||
|
%BREAKPOINT { LIT2r :debug JSR2r } |
||||||
|
|
||||||
|
@debug-start |
||||||
|
;debug-vector .System/vector DEO2 |
||||||
|
;debug-end JMP2 |
||||||
|
|
||||||
|
@debug ( pc* -- ) |
||||||
|
#0001 SUB2 .System/eaddr DEO2 |
||||||
|
.System/ecode DEIk #f8 AND #06 EOR SWP DEO |
||||||
|
,debug-vector/main JMP |
||||||
|
|
||||||
|
@debug-vector ( -> ) |
||||||
|
STH STH STH STH ( <- only run in case of working stack overflow ) |
||||||
|
&main |
||||||
|
|
||||||
|
( flush the working stack ) |
||||||
|
.System/wst DEI ;debug-wst/ptr STA |
||||||
|
&flush-wst |
||||||
|
.System/wst DEI #00 EQU ,&end-flush-wst JCN |
||||||
|
#00 .System/wst DEI #0002 SUB2 ;debug-wst/dat ADD2 STA |
||||||
|
,&flush-wst JMP |
||||||
|
&end-flush-wst |
||||||
|
|
||||||
|
( in case of working stack overflow, we need to append the four return stack bytes ) |
||||||
|
.System/ecode DEI #02 NEQ ,&skip-wst-append JCN |
||||||
|
#00 ;debug-wst/ptr LDAk ( 00 ptr-hi ptr-lo ptr / ... z y x w ) |
||||||
|
DUP #04 ADD OVR2 STA |
||||||
|
ROT ROT ADD2 ( start* / ... z y x w ) |
||||||
|
INC2 DUP2 #0004 ADD2 SWP2 ( end* start* / ... z y x w ) |
||||||
|
&loop |
||||||
|
DUP2 STHr ROT ROT STA |
||||||
|
INC2 |
||||||
|
GTH2k ,&loop JCN |
||||||
|
POP2 POP2 |
||||||
|
&skip-wst-append |
||||||
|
|
||||||
|
( flush the return stack ) |
||||||
|
.System/rst DEI ;debug-rst/ptr STA |
||||||
|
&flush-rst |
||||||
|
.System/rst DEI #00 EQU ,&end-flush-rst JCN |
||||||
|
STHr #00 .System/rst DEI ;debug-rst/dat ADD2 STA |
||||||
|
,&flush-rst JMP |
||||||
|
&end-flush-rst |
||||||
|
|
||||||
|
( Version 0.1 functionality: print the error and exit ) |
||||||
|
;debug-print-error JSR2 |
||||||
|
#01 .System/halt DEO |
||||||
|
BRK |
||||||
|
|
||||||
|
@debug-print-opcode ( instr -- ) |
||||||
|
DUP ,¬-brk JCN |
||||||
|
POP ;&brk-msg ;debug-print JMP2 ( tail call ) |
||||||
|
&brk-msg "BRK 00 |
||||||
|
¬-brk |
||||||
|
#00 OVR #1f AND #03 MUL ;&opcode-names ADD2 ( instr addr* ) |
||||||
|
LDAk .Console/write DEO INC2 |
||||||
|
LDAk .Console/write DEO INC2 |
||||||
|
LDA .Console/write DEO |
||||||
|
DUP #1f AND ,¬-lit JCN |
||||||
|
#7f AND |
||||||
|
¬-lit |
||||||
|
DUP #20 AND #00 EQU ,¬-2 JCN |
||||||
|
LIT '2 .Console/write DEO |
||||||
|
¬-2 |
||||||
|
DUP #80 AND #00 EQU ,¬-k JCN |
||||||
|
LIT 'k .Console/write DEO |
||||||
|
¬-k |
||||||
|
#40 AND #00 EQU ,¬-r JCN |
||||||
|
LIT 'r .Console/write DEO |
||||||
|
¬-r |
||||||
|
JMP2r |
||||||
|
|
||||||
|
&opcode-names |
||||||
|
"LITINCPOPDUPNIPSWPOVRROT |
||||||
|
"EQUNEQGTHLTHJMPJCNJSRSTH |
||||||
|
"LDZSTZLDRSTRLDASTADEIDEO |
||||||
|
"ADDSUBMULDIVANDORAEORSFT |
||||||
|
|
||||||
|
@debug-print ( addr* -- ) |
||||||
|
LDAk #00 EQU ,&end JCN |
||||||
|
LDAk .Console/write DEO |
||||||
|
INC2 |
||||||
|
,debug-print JMP |
||||||
|
&end POP2 JMP2r |
||||||
|
|
||||||
|
@debug-print-error |
||||||
|
;&halted-msg ,debug-print JSR |
||||||
|
#00 .System/ecode DEI #07 AND #20 SFT2 ;&messages-table ADD2 |
||||||
|
LDA2k ,debug-print JSR |
||||||
|
INC2 INC2 LDA2 ,debug-print JSR |
||||||
|
;&executing-msg ,debug-print JSR |
||||||
|
.System/eaddr DEI2 LDA ;debug-print-opcode JSR2 |
||||||
|
;&at-msg ,debug-print JSR |
||||||
|
.System/eaddr DEI2 ;debug-print-hex-short JSR2 |
||||||
|
#0a .Console/write DEO |
||||||
|
;&wst-msg ,debug-print JSR |
||||||
|
;&contents-msg ,debug-print JSR |
||||||
|
;debug-wst ;debug-print-stack JSR2 |
||||||
|
#0a .Console/write DEO |
||||||
|
;&rst-msg ,debug-print JSR |
||||||
|
;&contents-msg ,debug-print JSR |
||||||
|
;debug-rst ;debug-print-stack JSR2 |
||||||
|
#0a .Console/write DEO |
||||||
|
JMP2r |
||||||
|
|
||||||
|
&messages-table |
||||||
|
:&wst-msg :&underflow-msg |
||||||
|
:&rst-msg :&underflow-msg |
||||||
|
:&wst-msg :&overflow-msg |
||||||
|
:&rst-msg :&overflow-msg |
||||||
|
:&wst-msg :&divzero-msg |
||||||
|
:&rst-msg :&divzero-msg |
||||||
|
:&userdef-msg :&breakpoint-msg |
||||||
|
:&userdef-msg :&custom-msg |
||||||
|
|
||||||
|
&halted-msg "Halted: 2000 ( #0002, at 0x0100 ) |
||||||
|
&wst-msg "Working-stack 2000 |
||||||
|
&rst-msg "Return-stack 2000 |
||||||
|
&userdef-msg "User-defined 2000 |
||||||
|
&underflow-msg "underflow 00 |
||||||
|
&overflow-msg "overflow 00 |
||||||
|
&divzero-msg "division 20 "by 20 "zero 00 |
||||||
|
&breakpoint-msg "breakpoint 00 |
||||||
|
&custom-msg "custom 20 "error 00 |
||||||
|
&executing-msg 20 "executing 2000 |
||||||
|
&at-msg 20 "at 20 "0x 00 |
||||||
|
&contents-msg "contents: 00 |
||||||
|
|
||||||
|
@debug-print-hex-short ( value* -- ) |
||||||
|
SWP ,debug-print-hex-byte JSR |
||||||
|
( fall through ) |
||||||
|
|
||||||
|
@debug-print-hex-byte ( value -- ) |
||||||
|
DUP #04 SFT ,debug-print-hex-nibble JSR |
||||||
|
#0f AND |
||||||
|
( fall through ) |
||||||
|
|
||||||
|
@debug-print-hex-nibble ( value -- ) |
||||||
|
#30 ADD DUP #39 GTH #27 MUL ADD |
||||||
|
.Console/write DEO |
||||||
|
JMP2r |
||||||
|
|
||||||
|
@debug-print-stack ( addr* -- ) |
||||||
|
LDAk ,¬-empty JCN |
||||||
|
POP2 ;&empty-msg ;debug-print JMP2 ( tail call ) |
||||||
|
¬-empty |
||||||
|
LDAk STH INC2 ( dat* / count ) |
||||||
|
&loop |
||||||
|
STHkr #00 EQU ,&end JCN |
||||||
|
#20 .Console/write DEO |
||||||
|
LDAk ,debug-print-hex-byte JSR |
||||||
|
INC2 |
||||||
|
LITr 01 SUBr |
||||||
|
,&loop JMP |
||||||
|
&end |
||||||
|
POP2 POPr |
||||||
|
JMP2r |
||||||
|
|
||||||
|
&empty-msg 20 "(empty) 00 |
||||||
|
|
||||||
|
@debug-wst &ptr $1 &dat $ff |
||||||
|
@debug-rst &ptr $1 &dat $ff |
||||||
|
@debug-end |
||||||
|
|
||||||
@ -0,0 +1,270 @@ |
|||||||
|
( launcher ) |
||||||
|
|
||||||
|
%+ { ADD } %- { SUB } %* { MUL } %/ { DIV } |
||||||
|
%< { LTH } %> { GTH } %= { EQU } %! { NEQ } |
||||||
|
%++ { ADD2 } %-- { SUB2 } %** { MUL2 } %// { DIV2 } |
||||||
|
%<< { LTH2 } %>> { GTH2 } %== { EQU2 } %!! { NEQ2 } |
||||||
|
|
||||||
|
%AUTO-X { #01 .Screen/auto DEO } |
||||||
|
%AUTO-Y { #02 .Screen/auto DEO } |
||||||
|
%AUTO-YADDR { #06 .Screen/auto DEO } |
||||||
|
|
||||||
|
%HALT { #010f DEO } |
||||||
|
%EMIT { #18 DEO } |
||||||
|
%PRINT { ;print-str JSR2 #0a EMIT } |
||||||
|
%DEBUG { ;print-hex/byte JSR2 #0a EMIT } |
||||||
|
%DEBUG2 { ;print-hex JSR2 #0a EMIT } |
||||||
|
|
||||||
|
%MODALW { #0024 } |
||||||
|
%MODALH { #0009 } |
||||||
|
|
||||||
|
%RTN { JMP2r } |
||||||
|
%2// { #01 SFT2 } |
||||||
|
%8** { #30 SFT2 } |
||||||
|
|
||||||
|
%EADDR { #fd04 } |
||||||
|
%ECODE { #fd06 } |
||||||
|
|
||||||
|
( devices ) |
||||||
|
|
||||||
|
|00 @System &vector $2 &wst $1 &rst $1 &eaddr $2 &ecode $1 &pad $1 &r $2 &g $2 &b $2 &debug $1 &halt $1 |
||||||
|
|20 @Screen &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 |
||||||
|
|80 @Controller &vector $2 &button $1 &key $1 &func $1 |
||||||
|
|
||||||
|
( variables ) |
||||||
|
|
||||||
|
|0000 |
||||||
|
|
||||||
|
@center |
||||||
|
&x $2 &y $2 |
||||||
|
@modal |
||||||
|
&x $2 &y $2 |
||||||
|
|
||||||
|
( init ) |
||||||
|
|
||||||
|
|0100 ( -> ) |
||||||
|
|
||||||
|
.Screen/width DEI2 2// |
||||||
|
DUP2 .center/x STZ2 |
||||||
|
MODALW #31 SFT2 -- .modal/x STZ2 |
||||||
|
.Screen/height DEI2 2// |
||||||
|
DUP2 .center/y STZ2 |
||||||
|
MODALH #31 SFT2 -- .modal/y STZ2 |
||||||
|
|
||||||
|
( vectors ) |
||||||
|
;on-error .System/vector DEO2 |
||||||
|
;on-frame .Screen/vector DEO2 |
||||||
|
;on-button .Controller/vector DEO2 |
||||||
|
|
||||||
|
BRK |
||||||
|
|
||||||
|
@on-frame ( -> ) |
||||||
|
|
||||||
|
;draw-cross JSR2 |
||||||
|
;draw-stacks JSR2 |
||||||
|
|
||||||
|
BRK |
||||||
|
|
||||||
|
@on-button ( -> ) |
||||||
|
|
||||||
|
.Controller/func DEI |
||||||
|
DUP #02 ! ,&no-f2 JCN |
||||||
|
;toggle-debugger JSR2 |
||||||
|
&no-f2 |
||||||
|
DUP #08 ! ,&no-f4 JCN |
||||||
|
;reboot JSR2 |
||||||
|
&no-f4 |
||||||
|
POP |
||||||
|
|
||||||
|
BRK |
||||||
|
|
||||||
|
@on-error ( -> ) |
||||||
|
|
||||||
|
( background ) |
||||||
|
#00 .Screen/auto DEO |
||||||
|
;bg-icn .Screen/addr DEO2 |
||||||
|
MODALH #0000 |
||||||
|
&ver |
||||||
|
DUP2 8** .modal/y LDZ2 ++ .Screen/y DEO2 |
||||||
|
MODALW #0000 |
||||||
|
&hor |
||||||
|
DUP2 8** .modal/x LDZ2 ++ .Screen/x DEO2 |
||||||
|
#42 .Screen/sprite DEO |
||||||
|
INC2 GTH2k ,&hor JCN |
||||||
|
POP2 POP2 |
||||||
|
INC2 GTH2k ,&ver JCN |
||||||
|
POP2 POP2 |
||||||
|
|
||||||
|
( corners ) |
||||||
|
;corner-icn .Screen/addr DEO2 |
||||||
|
.modal/x LDZ2 .Screen/x DEO2 |
||||||
|
.modal/y LDZ2 .Screen/y DEO2 |
||||||
|
#42 .Screen/sprite DEO |
||||||
|
.modal/x LDZ2 MODALW #0001 -- 8** ++ .Screen/x DEO2 |
||||||
|
#52 .Screen/sprite DEO |
||||||
|
|
||||||
|
.modal/y LDZ2 MODALH #0001 -- 8** ++ .Screen/y DEO2 |
||||||
|
#72 .Screen/sprite DEO |
||||||
|
|
||||||
|
.modal/x LDZ2 .Screen/x DEO2 |
||||||
|
#62 .Screen/sprite DEO |
||||||
|
|
||||||
|
( text ) |
||||||
|
.modal/x LDZ2 #0010 ++ .Screen/x DEO2 |
||||||
|
.modal/y LDZ2 #0010 ++ .Screen/y DEO2 |
||||||
|
;error-txts/0 #4f ;draw-str JSR2 |
||||||
|
|
||||||
|
;at-txt #4f ;draw-str JSR2 |
||||||
|
|
||||||
|
EADDR LDA2 #47 ;draw-short JSR2 |
||||||
|
|
||||||
|
#0000 EADDR STA2 |
||||||
|
|
||||||
|
BRK |
||||||
|
|
||||||
|
@toggle-debugger ( -- ) |
||||||
|
|
||||||
|
( toggle debug ) #fd0e STH2k LDA #00 = STH2r STA |
||||||
|
|
||||||
|
RTN |
||||||
|
|
||||||
|
@reboot ( -- ) |
||||||
|
|
||||||
|
( clear devices/stacks ) |
||||||
|
#fd00 #0300 ;mclr JSR2 |
||||||
|
|
||||||
|
RTN |
||||||
|
&boot-path "boot.rom $1 |
||||||
|
|
||||||
|
@draw-stacks ( -- ) |
||||||
|
|
||||||
|
AUTO-YADDR |
||||||
|
#0010 #0000 |
||||||
|
&wst |
||||||
|
( working stack ) |
||||||
|
#0010 .Screen/y DEO2 |
||||||
|
DUP2 #0018 ** #0010 ++ .Screen/x DEO2 |
||||||
|
DUP #fe00 LDA ( ptr ) EQU #41 + STH |
||||||
|
DUP2 #fe01 ++ LDA STHr ;draw-byte JSR2 |
||||||
|
( return stack ) |
||||||
|
#0028 .Screen/y DEO2 |
||||||
|
DUP2 #0018 ** #0010 ++ .Screen/x DEO2 |
||||||
|
DUP #ff00 LDA ( ptr ) EQU #41 + STH |
||||||
|
DUP2 #ff01 ++ LDA STHr ;draw-byte JSR2 |
||||||
|
INC2 GTH2k ,&wst JCN |
||||||
|
POP2 POP2 |
||||||
|
|
||||||
|
RTN |
||||||
|
|
||||||
|
@draw-cross ( -- ) |
||||||
|
|
||||||
|
( ver ) |
||||||
|
AUTO-Y |
||||||
|
#0000 .Screen/y DEO2 |
||||||
|
.center/x LDZ2 .Screen/x DEO2 |
||||||
|
.Screen/height DEI2 #0000 |
||||||
|
&ver |
||||||
|
#43 .Screen/pixel DEO |
||||||
|
.Screen/y DEI2k INC2 ROT DEO2 |
||||||
|
INC2 GTH2k ,&ver JCN |
||||||
|
POP2 POP2 |
||||||
|
|
||||||
|
( hor ) |
||||||
|
AUTO-X |
||||||
|
#0000 .Screen/x DEO2 |
||||||
|
.center/y LDZ2 .Screen/y DEO2 |
||||||
|
.Screen/width DEI2 #0000 |
||||||
|
&hor |
||||||
|
#43 .Screen/pixel DEO |
||||||
|
.Screen/x DEI2k INC2 ROT DEO2 |
||||||
|
INC2 GTH2k ,&hor JCN |
||||||
|
POP2 POP2 |
||||||
|
|
||||||
|
RTN |
||||||
|
|
||||||
|
@draw-str ( text* color -- ) |
||||||
|
|
||||||
|
AUTO-YADDR |
||||||
|
STH |
||||||
|
&while |
||||||
|
LDAk STHkr ,draw-char JSR |
||||||
|
INC2 LDAk ,&while JCN |
||||||
|
POP2 |
||||||
|
POPr |
||||||
|
|
||||||
|
RTN |
||||||
|
|
||||||
|
@draw-short ( short* color -- ) |
||||||
|
|
||||||
|
STH SWP STHkr ,draw-byte JSR |
||||||
|
STHr ,draw-byte JSR |
||||||
|
|
||||||
|
RTN |
||||||
|
|
||||||
|
@draw-byte ( byte color -- ) |
||||||
|
|
||||||
|
STH |
||||||
|
DUP #04 SFT ,&parse JSR STHkr ,draw-char JSR |
||||||
|
#0f AND ,&parse JSR STHr ,draw-char JSR |
||||||
|
|
||||||
|
RTN |
||||||
|
&parse ( byte -- char ) DUP #09 GTH ,&above JCN #30 ADD JMP2r |
||||||
|
&above #57 ADD JMP2r |
||||||
|
|
||||||
|
@draw-char ( char color -- ) |
||||||
|
|
||||||
|
SWP |
||||||
|
[ #20 - #00 SWP #40 SFT2 ;font ++ ] .Screen/addr DEO2 |
||||||
|
.Screen/sprite DEOk DEO |
||||||
|
.Screen/x DEI2k #0008 ++ ROT DEO2 |
||||||
|
.Screen/y DEI2k #0010 -- ROT DEO2 |
||||||
|
|
||||||
|
JMP2r |
||||||
|
|
||||||
|
@mclr ( addr* len* -- ) |
||||||
|
|
||||||
|
OVR2 ++ SWP2 |
||||||
|
&loop |
||||||
|
STH2k #00 STH2r STA |
||||||
|
INC2 GTH2k ,&loop JCN |
||||||
|
POP2 POP2 |
||||||
|
|
||||||
|
JMP2r |
||||||
|
|
||||||
|
@print-hex ( value* -- ) |
||||||
|
|
||||||
|
SWP ,&byte JSR |
||||||
|
&byte ( byte -- ) |
||||||
|
STHk #04 SFT ,&parse JSR #18 DEO |
||||||
|
STHr #0f AND ,&parse JSR #18 DEO |
||||||
|
JMP2r |
||||||
|
&parse ( byte -- char ) DUP #09 GTH ,&above JCN #30 ADD JMP2r |
||||||
|
&above #57 ADD JMP2r |
||||||
|
|
||||||
|
JMP2r |
||||||
|
|
||||||
|
@print-str ( string* -- ) |
||||||
|
|
||||||
|
#0001 SUB2 |
||||||
|
&while |
||||||
|
INC2 LDAk DUP #18 DEO ,&while JCN |
||||||
|
POP2 |
||||||
|
|
||||||
|
JMP2r |
||||||
|
|
||||||
|
@error-txts |
||||||
|
&0 "Working-stack 20 "underflow $1 |
||||||
|
&1 "Return-stack 20 "underflow $1 |
||||||
|
&2 "Working-stack 20 "overflow $1 |
||||||
|
&3 "Return-stack 20 "overflow $1 |
||||||
|
&4 "Working-stack 20 "division 20 "by 20 "zero $1 |
||||||
|
&5 "Return-stack 20 "division 20 "by 20 "zero $1 |
||||||
|
@at-txt |
||||||
|
', 20 "at 20 $1 |
||||||
|
|
||||||
|
@bg-icn |
||||||
|
ffff ffff ffff ffff |
||||||
|
@corner-icn |
||||||
|
1f7f 7fff ffff ffff |
||||||
|
|
||||||
|
~projects/assets/msx01x02.tal |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
#include <time.h> |
||||||
|
|
||||||
|
#include "../uxn.h" |
||||||
|
#include "datetime.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2021 Devine Lu Linvega |
||||||
|
Copyright (c) 2021 Andrew Alderwick |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any |
||||||
|
purpose with or without fee is hereby granted, provided that the above |
||||||
|
copyright notice and this permission notice appear in all copies. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||||
|
WITH REGARD TO THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
Uint8 |
||||||
|
datetime_dei(Device *d, Uint8 port) |
||||||
|
{ |
||||||
|
time_t seconds = time(NULL); |
||||||
|
struct tm zt = {0}; |
||||||
|
struct tm *t = localtime(&seconds); |
||||||
|
if(t == NULL) |
||||||
|
t = &zt; |
||||||
|
switch(port) { |
||||||
|
case 0x0: return (t->tm_year + 1900) >> 8; |
||||||
|
case 0x1: return (t->tm_year + 1900); |
||||||
|
case 0x2: return t->tm_mon; |
||||||
|
case 0x3: return t->tm_mday; |
||||||
|
case 0x4: return t->tm_hour; |
||||||
|
case 0x5: return t->tm_min; |
||||||
|
case 0x6: return t->tm_sec; |
||||||
|
case 0x7: return t->tm_wday; |
||||||
|
case 0x8: return t->tm_yday >> 8; |
||||||
|
case 0x9: return t->tm_yday; |
||||||
|
case 0xa: return t->tm_isdst; |
||||||
|
default: return d->dat[port]; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
/*
|
||||||
|
Copyright (c) 2021 Devine Lu Linvega |
||||||
|
Copyright (c) 2021 Andrew Alderwick |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any |
||||||
|
purpose with or without fee is hereby granted, provided that the above |
||||||
|
copyright notice and this permission notice appear in all copies. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||||
|
WITH REGARD TO THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
Uint8 datetime_dei(Device *d, Uint8 port); |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
#include "../uxn.h" |
||||||
|
#include "system.h" |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any |
||||||
|
purpose with or without fee is hereby granted, provided that the above |
||||||
|
copyright notice and this permission notice appear in all copies. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||||
|
WITH REGARD TO THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
Uxn supervisor; |
||||||
|
|
||||||
|
static const char *errors[] = { |
||||||
|
"Working-stack underflow", |
||||||
|
"Return-stack underflow", |
||||||
|
"Working-stack overflow", |
||||||
|
"Return-stack overflow", |
||||||
|
"Working-stack division by zero", |
||||||
|
"Return-stack division by zero"}; |
||||||
|
|
||||||
|
int |
||||||
|
uxn_halt(Uxn *u, Uint8 error, Uint16 addr) |
||||||
|
{ |
||||||
|
Device *d = &u->dev[0]; |
||||||
|
Uint16 vec = d->vector; |
||||||
|
DEVPOKE16(0x4, addr); |
||||||
|
d->dat[0x6] = error; |
||||||
|
uxn_eval(&supervisor, supervisor.dev[0].vector); |
||||||
|
if(vec) { |
||||||
|
d->vector = 0; /* need to rearm to run System/vector again */ |
||||||
|
if(error != 2) /* working stack overflow has special treatment */ |
||||||
|
vec += 0x0004; |
||||||
|
return uxn_eval(u, vec); |
||||||
|
} |
||||||
|
fprintf(stderr, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* IO */ |
||||||
|
|
||||||
|
Uint8 |
||||||
|
system_dei(Device *d, Uint8 port) |
||||||
|
{ |
||||||
|
switch(port) { |
||||||
|
case 0x2: return d->u->wst->ptr; |
||||||
|
case 0x3: return d->u->rst->ptr; |
||||||
|
default: return d->dat[port]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
system_deo(Device *d, Uint8 port) |
||||||
|
{ |
||||||
|
switch(port) { |
||||||
|
case 0x1: DEVPEEK16(d->vector, 0x0); break; |
||||||
|
case 0x2: d->u->wst->ptr = d->dat[port]; break; |
||||||
|
case 0x3: d->u->rst->ptr = d->dat[port]; break; |
||||||
|
default: system_deo_special(d, port); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
/*
|
||||||
|
Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any |
||||||
|
purpose with or without fee is hereby granted, provided that the above |
||||||
|
copyright notice and this permission notice appear in all copies. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||||
|
WITH REGARD TO THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
Uint8 system_dei(Device *d, Uint8 port); |
||||||
|
void system_deo(Device *d, Uint8 port); |
||||||
|
void system_deo_special(Device *d, Uint8 port); |
||||||
|
|
||||||
|
extern Uxn supervisor; |
||||||
Loading…
Reference in new issue