Browse code

Added src/uxn-fast.c generator script

Andrew Alderwick authored on 23/05/2021 16:33:00
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,368 @@
1
+local replacements = {
2
+  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); }',
3
+  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); }',
4
+  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); }',
5
+  op_lit16 = '{ push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); }',
6
+  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); }',
7
+  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); }',
8
+  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); }',
9
+  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); }',
10
+  op_sth16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->dst, b); push8(u->dst, a); }'
11
+}
12
+local top, bottom, pushtop
13
+local offset
14
+offset = function(n, s)
15
+  if s == nil then
16
+    s = ''
17
+  end
18
+  if n < 0 then
19
+    return (' -%s %d'):format(s, -n)
20
+  elseif n > 0 then
21
+    return (' +%s %d'):format(s, n)
22
+  elseif s ~= '' then
23
+    return (' +%s 0'):format(s)
24
+  else
25
+    return ''
26
+  end
27
+end
28
+local pop_push
29
+pop_push = function(k, n, s)
30
+  local _exp_0 = k
31
+  if 'pop' == _exp_0 then
32
+    s = s:match('^%((%S+)%)$')
33
+    assert(s == 'src')
34
+    local _exp_1 = n
35
+    if '8' == _exp_1 then
36
+      top[s] = top[s] - 1
37
+      if bottom[s] > top[s] then
38
+        bottom[s] = top[s]
39
+      end
40
+      return ('%s.dat[%s.ptr%s]'):format(s, s, offset(top[s]))
41
+    elseif '16' == _exp_1 then
42
+      top[s] = top[s] - 2
43
+      if bottom[s] > top[s] then
44
+        bottom[s] = top[s]
45
+      end
46
+      return ('(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'):format(s, s, offset(top[s] + 1), s, s, offset(top[s]))
47
+    end
48
+  elseif 'push' == _exp_0 then
49
+    local v
50
+    s, v = s:match('^%((%S+), (.*)%)$')
51
+    assert(s == 'src' or s == 'dst', s)
52
+    local _exp_1 = n
53
+    if '8' == _exp_1 then
54
+      pushtop[s] = pushtop[s] + 1
55
+      return ('%s.dat[%s.ptr%s] = %s'):format(s, s, offset(pushtop[s] - 1), v)
56
+    elseif '16' == _exp_1 then
57
+      if v:match('%+%+') or v:match('%-%-') then
58
+        error('push16 has side effects: ' .. v)
59
+      end
60
+      local peek, args = v:match('^([md]e[mv]peek)16(%b())$')
61
+      if peek then
62
+        args = args:sub(2, -2)
63
+        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))
64
+      end
65
+      pushtop[s] = pushtop[s] + 2
66
+      if v:match(' ') then
67
+        v = '(' .. v .. ')'
68
+      end
69
+      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)
70
+    end
71
+  else
72
+    return nil
73
+  end
74
+end
75
+local process
76
+process = function(body)
77
+  local out_body = body:gsub('^%{ *', ''):gsub(' *%}$', ''):gsub('; ', ';\n'):gsub('(%a+)(%d+)(%b())', pop_push)
78
+  local in_ifdef = false
79
+  local _list_0 = {
80
+    'src',
81
+    'dst'
82
+  }
83
+  for _index_0 = 1, #_list_0 do
84
+    local k = _list_0[_index_0]
85
+    if bottom[k] ~= 0 then
86
+      if not in_ifdef then
87
+        out_body = out_body .. '\n#ifndef NO_STACK_CHECKS'
88
+        in_ifdef = true
89
+      end
90
+      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)
91
+    end
92
+    if pushtop[k] ~= 0 then
93
+      if pushtop[k] > 0 then
94
+        if not in_ifdef then
95
+          out_body = out_body .. '\n#ifndef NO_STACK_CHECKS'
96
+          in_ifdef = true
97
+        end
98
+        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)
99
+      end
100
+      if in_ifdef then
101
+        out_body = out_body .. '\n#endif'
102
+        in_ifdef = false
103
+      end
104
+      out_body = out_body .. ('\n%s.ptr %s= %d;'):format(k, pushtop[k] < 0 and '-' or '+', math.abs(pushtop[k]))
105
+    end
106
+  end
107
+  if in_ifdef then
108
+    out_body = out_body .. '\n#endif'
109
+    in_ifdef = false
110
+  end
111
+  local t = { }
112
+  out_body:gsub('[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', function(v, k)
113
+    t[k] = v
114
+  end)
115
+  out_body = out_body:gsub('(src%.dat%[[^]]+%]) = ([a-f]);\n', function(k, v)
116
+    if t[k] and t[k] == v then
117
+      return ''
118
+    end
119
+    return nil
120
+  end)
121
+  return out_body
122
+end
123
+local ops = { }
124
+for l in assert(io.lines('src/uxn.c')) do
125
+  local _continue_0 = false
126
+  repeat
127
+    local name, body = l:match('void (op_%S*)%(Uxn %*u%) (%b{})')
128
+    if not name then
129
+      _continue_0 = true
130
+      break
131
+    end
132
+    if replacements[name] then
133
+      body = replacements[name]
134
+    end
135
+    body = body:gsub('u%-%>src', 'src')
136
+    body = body:gsub('u%-%>dst', 'dst')
137
+    top = {
138
+      src = 0,
139
+      dst = 0
140
+    }
141
+    bottom = {
142
+      src = 0,
143
+      dst = 0
144
+    }
145
+    pushtop = top
146
+    ops[name] = process(body)
147
+    top = {
148
+      src = 0,
149
+      dst = 0
150
+    }
151
+    bottom = {
152
+      src = 0,
153
+      dst = 0
154
+    }
155
+    pushtop = {
156
+      src = 0,
157
+      dst = 0
158
+    }
159
+    ops['keep_' .. name] = process(body)
160
+    _continue_0 = true
161
+  until true
162
+  if not _continue_0 then
163
+    break
164
+  end
165
+end
166
+local dump
167
+dump = function(s, src, dst)
168
+  local ret = '\t\t\t{\n'
169
+  for l in s:gmatch('[^\n]+') do
170
+    if not l:match('^%#') then
171
+      ret = ret .. '\t\t\t\t'
172
+    end
173
+    ret = ret .. ('%s\n'):format(l)
174
+  end
175
+  ret = ret .. '\t\t\t}\n\t\t\tbreak;\n'
176
+  return (ret:gsub('src', src):gsub('dst', dst))
177
+end
178
+local i = 0
179
+local allops = { }
180
+local wanted = false
181
+for l in assert(io.lines('src/uxn.c')) do
182
+  if l == 'void (*ops[])(Uxn *u) = {' then
183
+    wanted = true
184
+  elseif l == '};' then
185
+    wanted = false
186
+  elseif wanted then
187
+    l = l:gsub('%/%b**%/', '')
188
+    for op in l:gmatch('[%w_]+') do
189
+      if not ops[op] then
190
+        error('missing ' .. op)
191
+      end
192
+      allops[i + 0x00 + 1] = {
193
+        n = {
194
+          i + 0x00
195
+        },
196
+        body = dump(ops[op], 'u->wst', 'u->rst')
197
+      }
198
+      allops[i + 0x40 + 1] = {
199
+        n = {
200
+          i + 0x40
201
+        },
202
+        body = dump(ops[op], 'u->rst', 'u->wst')
203
+      }
204
+      allops[i + 0x80 + 1] = {
205
+        n = {
206
+          i + 0x80
207
+        },
208
+        body = dump(ops['keep_' .. op], 'u->wst', 'u->rst')
209
+      }
210
+      allops[i + 0xc0 + 1] = {
211
+        n = {
212
+          i + 0xc0
213
+        },
214
+        body = dump(ops['keep_' .. op], 'u->rst', 'u->wst')
215
+      }
216
+      i = i + 1
217
+    end
218
+  end
219
+end
220
+i = 0
221
+wanted = false
222
+for l in assert(io.lines('src/assembler.c')) do
223
+  if l == 'char ops[][4] = {' then
224
+    wanted = true
225
+  elseif l == '};' then
226
+    wanted = false
227
+  elseif wanted then
228
+    for op in l:gmatch('"(...)"') do
229
+      i = i + 1
230
+      allops[i + 0x00].name = op
231
+      allops[i + 0x20].name = op .. '2'
232
+      allops[i + 0x40].name = op .. 'r'
233
+      allops[i + 0x60].name = op .. '2r'
234
+      allops[i + 0x80].name = op .. 'k'
235
+      allops[i + 0xa0].name = op .. '2k'
236
+      allops[i + 0xc0].name = op .. 'kr'
237
+      allops[i + 0xe0].name = op .. '2kr'
238
+    end
239
+  end
240
+end
241
+for i = 1, 256 do
242
+  local _continue_0 = false
243
+  repeat
244
+    if not allops[i] then
245
+      _continue_0 = true
246
+      break
247
+    end
248
+    for j = i + 1, 256 do
249
+      if allops[i].body == allops[j].body then
250
+        table.insert(allops[i].n, (table.remove(allops[j].n)))
251
+        allops[j].body = nil
252
+      end
253
+    end
254
+    _continue_0 = true
255
+  until true
256
+  if not _continue_0 then
257
+    break
258
+  end
259
+end
260
+do
261
+  local _with_0 = assert(io.open('src/uxn-fast.c', 'w'))
262
+  local f = assert(io.open('src/uxn.c'))
263
+  while true do
264
+    local l = f:read('*l')
265
+    _with_0:write(('%s\n'):format(l))
266
+    if l == '*/' then
267
+      break
268
+    end
269
+  end
270
+  _with_0:write('\n')
271
+  _with_0:write([[/*
272
+ ^
273
+/!\ THIS FILE IS AUTOMATICALLY GENERATED
274
+---
275
+
276
+Its contents can get overwritten with the processed contents of src/uxn.c.
277
+See etc/mkuxn-fast.moon for instructions.
278
+
279
+*/
280
+]])
281
+  while true do
282
+    local _continue_0 = false
283
+    repeat
284
+      local l = f:read('*l')
285
+      if l:match(' push') or l:match('[ *]pop') then
286
+        _continue_0 = true
287
+        break
288
+      end
289
+      if l == '/* Stack */' then
290
+        break
291
+      end
292
+      _with_0:write(('%s\n'):format(l))
293
+      _continue_0 = true
294
+    until true
295
+    if not _continue_0 then
296
+      break
297
+    end
298
+  end
299
+  _with_0:write([[/* clang-format on */
300
+
301
+#pragma mark - Core
302
+
303
+int
304
+evaluxn(Uxn *u, Uint16 vec)
305
+{
306
+	Uint8 instr;
307
+	u->ram.ptr = vec;
308
+	while(u->ram.ptr) {
309
+		instr = u->ram.dat[u->ram.ptr++];
310
+		switch(instr) {
311
+#pragma GCC diagnostic push
312
+#pragma GCC diagnostic ignored "-Wunused-value"
313
+#pragma GCC diagnostic ignored "-Wunused-variable"
314
+]])
315
+  for i = 1, 256 do
316
+    local _continue_0 = false
317
+    repeat
318
+      if not allops[i].body then
319
+        _continue_0 = true
320
+        break
321
+      end
322
+      local _list_0 = allops[i].n
323
+      for _index_0 = 1, #_list_0 do
324
+        local n = _list_0[_index_0]
325
+        _with_0:write(('\t\tcase 0x%02x: /* %s */\n'):format(n, allops[n + 1].name))
326
+      end
327
+      _with_0:write(('\t\t\t__asm__( "evaluxn_%02x_%s:" );\n'):format(allops[i].n[1], allops[i].name))
328
+      _with_0:write(allops[i].body)
329
+      _continue_0 = true
330
+    until true
331
+    if not _continue_0 then
332
+      break
333
+    end
334
+  end
335
+  _with_0:write([[#pragma GCC diagnostic pop
336
+		}
337
+	}
338
+	return 1;
339
+#ifndef NO_STACK_CHECKS
340
+error:
341
+	printf("Halted: %s-stack %sflow#%04x, at 0x%04x\n",
342
+		u->wst.error ? "Working" : "Return",
343
+		((u->wst.error | u->rst.error) & 2) ? "over" : "under",
344
+		instr,
345
+		u->ram.ptr);
346
+	return 0;
347
+#endif
348
+}
349
+
350
+int
351
+]])
352
+  wanted = false
353
+  while true do
354
+    local l = f:read('*l')
355
+    if not l then
356
+      break
357
+    end
358
+    if l:match('^bootuxn') then
359
+      wanted = true
360
+    end
361
+    if wanted then
362
+      _with_0:write(('%s\n'):format(l))
363
+    end
364
+  end
365
+  f:close()
366
+  _with_0:close()
367
+  return _with_0
368
+end
0 369
new file mode 100644
... ...
@@ -0,0 +1,263 @@
1
+--
2
+-- Uxn core unroller script
3
+--
4
+-- This script updates src/uxn-fast.c when Uxn's opcode set changes, so that
5
+-- updates in the human-readable src/uxn.c core can be easily converted into
6
+-- high-performance code.
7
+--
8
+-- To run, you need Lua or LuaJIT, and just run etc/mkuxn-fast.lua from the top
9
+-- directory of Uxn's git repository:
10
+--
11
+--     lua etc/mkuxn-fast.lua
12
+--
13
+-- This file is written in MoonScript, which is a language that compiles to
14
+-- Lua, the same way as e.g. CoffeeScript compiles to JavaScript. Since
15
+-- installing MoonScript has more dependencies than Lua, the compiled
16
+-- etc/mkuxn-fast.lua is kept in Uxn's repository and will be kept updated as
17
+-- this file changes.
18
+--
19
+
20
+replacements =
21
+	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); }'
22
+	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); }'
23
+	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); }'
24
+	op_lit16: '{ push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); }'
25
+	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); }'
26
+	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); }'
27
+	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); }'
28
+	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); }'
29
+	op_sth16: '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->dst, b); push8(u->dst, a); }'
30
+
31
+local top, bottom, pushtop
32
+
33
+offset = (n, s = '') ->
34
+	if n < 0
35
+		' -%s %d'\format s, -n
36
+	elseif n > 0
37
+		' +%s %d'\format s, n
38
+	elseif s != ''
39
+		' +%s 0'\format s
40
+	else
41
+		''
42
+
43
+pop_push = (k, n, s) ->
44
+	switch k
45
+		when 'pop'
46
+			s = s\match '^%((%S+)%)$'
47
+			assert s == 'src'
48
+			switch n
49
+				when '8'
50
+					top[s] -= 1
51
+					if bottom[s] > top[s]
52
+						bottom[s] = top[s]
53
+					'%s.dat[%s.ptr%s]'\format s, s, offset(top[s])
54
+				when '16'
55
+					top[s] -= 2
56
+					if bottom[s] > top[s]
57
+						bottom[s] = top[s]
58
+					'(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'\format s, s, offset(top[s] + 1), s, s, offset(top[s])
59
+		when 'push'
60
+			s, v = s\match '^%((%S+), (.*)%)$'
61
+			assert s == 'src' or s == 'dst', s
62
+			switch n
63
+				when '8'
64
+					pushtop[s] += 1
65
+					'%s.dat[%s.ptr%s] = %s'\format s, s, offset(pushtop[s] - 1), v
66
+				when '16'
67
+					if v\match'%+%+' or v\match'%-%-'
68
+						error 'push16 has side effects: ' .. v
69
+					peek, args = v\match '^([md]e[mv]peek)16(%b())$'
70
+					if peek
71
+						args = args\sub 2, -2
72
+						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)
73
+					pushtop[s] += 2
74
+					if v\match ' '
75
+						v = '(' .. v .. ')'
76
+					'%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
77
+		else
78
+			nil
79
+
80
+process = (body) ->
81
+	out_body = body\gsub('^%{ *', '')\gsub(' *%}$', '')\gsub('; ', ';\n')\gsub '(%a+)(%d+)(%b())', pop_push
82
+	in_ifdef = false
83
+	for k in *{'src', 'dst'}
84
+		if bottom[k] != 0
85
+			if not in_ifdef
86
+				out_body ..= '\n#ifndef NO_STACK_CHECKS'
87
+				in_ifdef = true
88
+			out_body ..= '\nif(__builtin_expect(%s.ptr < %d, 0)) {\n\t%s.error = 1;\n\tgoto error;\n}'\format k, -bottom[k], k
89
+		if pushtop[k] != 0
90
+			if pushtop[k] > 0
91
+				if not in_ifdef
92
+					out_body ..= '\n#ifndef NO_STACK_CHECKS'
93
+					in_ifdef = true
94
+				out_body ..= '\nif(__builtin_expect(%s.ptr > %d, 0)) {\n\t%s.error = 2;\n\tgoto error;\n}'\format k, 255 - pushtop[k], k
95
+			if in_ifdef
96
+				out_body ..= '\n#endif'
97
+				in_ifdef = false
98
+			out_body ..= '\n%s.ptr %s= %d;'\format k, pushtop[k] < 0 and '-' or '+', math.abs pushtop[k]
99
+	if in_ifdef
100
+		out_body ..= '\n#endif'
101
+		in_ifdef = false
102
+	t = {}
103
+	out_body\gsub '[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', (v, k) -> t[k] = v
104
+	out_body = out_body\gsub '(src%.dat%[[^]]+%]) = ([a-f]);\n', (k, v) ->
105
+		if t[k] and t[k] == v
106
+			return ''
107
+		return nil
108
+	out_body
109
+
110
+ops = {}
111
+
112
+for l in assert io.lines 'src/uxn.c'
113
+	name, body = l\match 'void (op_%S*)%(Uxn %*u%) (%b{})'
114
+	if not name
115
+		continue
116
+	if replacements[name]
117
+		body = replacements[name]
118
+	body = body\gsub 'u%-%>src', 'src'
119
+	body = body\gsub 'u%-%>dst', 'dst'
120
+	top = { src: 0, dst: 0 }
121
+	bottom = { src: 0, dst: 0 }
122
+	pushtop = top
123
+	ops[name] = process body
124
+	top = { src: 0, dst: 0 }
125
+	bottom = { src: 0, dst: 0 }
126
+	pushtop = { src: 0, dst: 0 }
127
+	ops['keep_' .. name] = process body
128
+
129
+dump = (s, src, dst) ->
130
+	ret = '\t\t\t{\n'
131
+	for l in s\gmatch '[^\n]+'
132
+		if not l\match '^%#'
133
+			ret ..= '\t\t\t\t'
134
+		ret ..= '%s\n'\format l
135
+	ret ..= '\t\t\t}\n\t\t\tbreak;\n'
136
+	(ret\gsub('src', src)\gsub('dst', dst))
137
+
138
+i = 0
139
+allops = {}
140
+wanted = false
141
+for l in assert io.lines 'src/uxn.c'
142
+	if l == 'void (*ops[])(Uxn *u) = {'
143
+		wanted = true
144
+	elseif l == '};'
145
+		wanted = false
146
+	elseif wanted
147
+		l = l\gsub '%/%b**%/', ''
148
+		for op in l\gmatch '[%w_]+'
149
+			if not ops[op]
150
+				error 'missing ' .. op
151
+			allops[i + 0x00 + 1] = { n: { i + 0x00 }, body: dump ops[op], 'u->wst', 'u->rst' }
152
+			allops[i + 0x40 + 1] = { n: { i + 0x40 }, body: dump ops[op], 'u->rst', 'u->wst' }
153
+			allops[i + 0x80 + 1] = { n: { i + 0x80 }, body: dump ops['keep_' .. op], 'u->wst', 'u->rst' }
154
+			allops[i + 0xc0 + 1] = { n: { i + 0xc0 }, body: dump ops['keep_' .. op], 'u->rst', 'u->wst' }
155
+			i += 1
156
+
157
+i = 0
158
+wanted = false
159
+for l in assert io.lines 'src/assembler.c'
160
+	if l == 'char ops[][4] = {'
161
+		wanted = true
162
+	elseif l == '};'
163
+		wanted = false
164
+	elseif wanted
165
+		for op in l\gmatch '"(...)"'
166
+			i += 1
167
+			allops[i + 0x00].name = op
168
+			allops[i + 0x20].name = op .. '2'
169
+			allops[i + 0x40].name = op .. 'r'
170
+			allops[i + 0x60].name = op .. '2r'
171
+			allops[i + 0x80].name = op .. 'k'
172
+			allops[i + 0xa0].name = op .. '2k'
173
+			allops[i + 0xc0].name = op .. 'kr'
174
+			allops[i + 0xe0].name = op .. '2kr'
175
+
176
+for i = 1, 256
177
+	if not allops[i]
178
+		continue
179
+	for j = i + 1, 256
180
+		if allops[i].body == allops[j].body
181
+			table.insert allops[i].n, (table.remove allops[j].n)
182
+			allops[j].body = nil
183
+
184
+with assert io.open 'src/uxn-fast.c', 'w'
185
+	f = assert io.open 'src/uxn.c'
186
+	while true
187
+		l = f\read '*l'
188
+		\write '%s\n'\format l
189
+		if l == '*/'
190
+			break
191
+	\write '\n'
192
+	\write [[
193
+/*
194
+ ^
195
+/!\ THIS FILE IS AUTOMATICALLY GENERATED
196
+---
197
+
198
+Its contents can get overwritten with the processed contents of src/uxn.c.
199
+See etc/mkuxn-fast.moon for instructions.
200
+
201
+*/
202
+]]
203
+	while true
204
+		l = f\read '*l'
205
+		if l\match' push' or l\match'[ *]pop'
206
+			continue
207
+		if l == '/* Stack */'
208
+			break
209
+		\write '%s\n'\format l
210
+	\write [[
211
+/* clang-format on */
212
+
213
+#pragma mark - Core
214
+
215
+int
216
+evaluxn(Uxn *u, Uint16 vec)
217
+{
218
+	Uint8 instr;
219
+	u->ram.ptr = vec;
220
+	while(u->ram.ptr) {
221
+		instr = u->ram.dat[u->ram.ptr++];
222
+		switch(instr) {
223
+#pragma GCC diagnostic push
224
+#pragma GCC diagnostic ignored "-Wunused-value"
225
+#pragma GCC diagnostic ignored "-Wunused-variable"
226
+]]
227
+	for i = 1, 256
228
+		if not allops[i].body
229
+			continue
230
+		for n in *allops[i].n
231
+			\write '\t\tcase 0x%02x: /* %s */\n'\format n, allops[n + 1].name
232
+		\write '\t\t\t__asm__( "evaluxn_%02x_%s:" );\n'\format allops[i].n[1], allops[i].name
233
+		\write allops[i].body
234
+	\write [[
235
+#pragma GCC diagnostic pop
236
+		}
237
+	}
238
+	return 1;
239
+#ifndef NO_STACK_CHECKS
240
+error:
241
+	printf("Halted: %s-stack %sflow#%04x, at 0x%04x\n",
242
+		u->wst.error ? "Working" : "Return",
243
+		((u->wst.error | u->rst.error) & 2) ? "over" : "under",
244
+		instr,
245
+		u->ram.ptr);
246
+	return 0;
247
+#endif
248
+}
249
+
250
+int
251
+]]
252
+	wanted = false
253
+	while true
254
+		l = f\read '*l'
255
+		if not l
256
+			break
257
+		if l\match '^bootuxn'
258
+			wanted = true
259
+		if wanted
260
+			\write '%s\n'\format l
261
+	f\close!
262
+	\close!
263
+
... ...
@@ -13,6 +13,18 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 13
 WITH REGARD TO THIS SOFTWARE.
14 14
 */
15 15
 
16
+/*
17
+ ^
18
+/!\ THIS FILE IS AUTOMATICALLY GENERATED
19
+---
20
+
21
+Its contents can get overwritten with the processed contents of src/uxn.c.
22
+See etc/mkuxn-fast.moon for instructions.
23
+
24
+*/
25
+
26
+#pragma mark - Operations
27
+
16 28
 /* clang-format off */
17 29
 void   mempoke8(Uint8 *m, Uint16 a, Uint8 b) { m[a] = b; }
18 30
 Uint8  mempeek8(Uint8 *m, Uint16 a) { return m[a]; }
... ...
@@ -24,6 +36,8 @@ void   devpoke16(Device *d, Uint8 a, Uint16 b) { devpoke8(d, a, b >> 8); devpoke
24 36
 Uint16 devpeek16(Device *d, Uint16 a) { return (devpeek8(d, a) << 8) + devpeek8(d, a + 1); }
25 37
 /* clang-format on */
26 38
 
39
+#pragma mark - Core
40
+
27 41
 int
28 42
 evaluxn(Uxn *u, Uint16 vec)
29 43
 {
... ...
@@ -3,6 +3,7 @@
3 3
 
4 4
 /*
5 5
 Copyright (u) 2021 Devine Lu Linvega
6
+Copyright (u) 2021 Andrew Alderwick
6 7
 
7 8
 Permission to use, copy, modify, and distribute this software for any
8 9
 purpose with or without fee is hereby granted, provided that the above
... ...
@@ -176,8 +177,10 @@ int
176 177
 loaduxn(Uxn *u, char *filepath)
177 178
 {
178 179
 	FILE *f;
179
-	if(!(f = fopen(filepath, "rb")))
180
-		return haltuxn(u, "Missing input rom.", 0);
180
+	if(!(f = fopen(filepath, "rb"))) {
181
+		printf("Halted: Missing input rom.\n");
182
+		return 0;
183
+	}
181 184
 	fread(u->ram.dat + PAGE_PROGRAM, sizeof(u->ram.dat) - PAGE_PROGRAM, 1, f);
182 185
 	printf("Uxn loaded[%s].\n", filepath);
183 186
 	return 1;