io.stdout:setvbuf'no'
io.stderr:setvbuf'no'

local dynasm = require'dynasm'
local dasm = require'dasm'

--set the globals returned from the dasl module

local demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL
local function set_vars(t)
	demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL = unpack(t)
end

--load dasl files via loadfile() and via require().

--load and run the dasl file from the current directory.
local function load_via_loadfile()
	set_vars(assert(dynasm.loadfile'dynasm_demo_x86.dasl')())
end

--load the same file via require() from package.path.
local function load_via_require()
	set_vars(require'dynasm_demo_x86')
end

--helpers

local function hr() return ('-'):rep(60) end
local function printf(...) print(string.format(...)) end

--assemble a demo from the dasl file, dump it and run it
local function run_demo(name)
	collectgarbage() --clean up from the last session

	local gencode = demo[name]

	--make a new dasm state
	local state, globals = dasm.new(actions, externnames, DASM_MAXSECTION, DASM_MAXGLOBAL)

	--generate the code and get the test function for that code
	local runcode = gencode(state)

	--build the code
	local buf, size = state:build()

	--show code and size
	printf('%-16s %-10s %s', 'code address', '', tostring(buf))
	printf('%-16s %-10d %s', 'code size', size, 'bytes')

	--show the labels from this code
	for i = 0, #globalnames do --from .globalnames directive
		if globals[i] ~= nil then
			printf('%-16s %-10s %s', 'global', globalnames[i], globals[i])
		end
	end

	--dump the code
	print(hr())
	dasm.dump(buf, size)

	--run the code
	if runcode then
		runcode(buf)
	end
end

--run all demos
local function run_all_demos()
	for i,name in ipairs(demos) do
		print()
		print(name)
		print(hr())
		run_demo(name)
	end
end

local function test_loadstring()
	local chunk = dynasm.loadstring[[

local ffi = require'ffi'
local dasm = require'dasm'

|.arch ARCH
|.actionlist actions

local function gen()
	|  mov ax, bx
end

return actions
]]
	local ffi = require'ffi'
	local actions = chunk()
	print()
	print('loadstring test: actionlist for `mov ax, bx`:')
	print(hr())
	print(string.byte(ffi.string(actions, ffi.sizeof(actions)), 1, ffi.sizeof(actions)))
end

local function test_translate_tostring()
	print()
	print'translate_tostring test:'
	print(hr())
	local asm = [[

local ffi = require'ffi'
local dasm = require'dasm'

|.arch ARCH

]]
	print(dynasm.translate_tostring(dynasm.string_infile(asm), {lang = "lua"}))
end

local function run_default()
	load_via_loadfile()
	run_all_demos()
	--we're loading the same file again to test the reusability of dynasm.
	--there's a lot of global state in dynasm which needs to be reset between runs.
	load_via_require()
	run_all_demos()
	--additional tests...
	test_loadstring()
	test_translate_tostring()
end

if not ... then
	run_default()
else
	load_via_require()
	run_demo((...))
end
