TPT Script Server

This page is used for previewing and submitting scripts for use with the Script Manager

Available Scripts

(2) TPTMulti by LBPHacker
(3) set wifi v2 by jacob1
(4) Script Paste by jacob1
(5) Random Element by jacob1
(6) Magical Merge Master 3000 by nucular
(7) More Fuel Mod-heavy by jward212
(9) Breakpoints (BRPT) by boxmein
(10) Cockroaches! by boxmein
(11) Random tree by ssccsscc
(12) Element with random properties by ssccsscc
(15) Minimalistic Element Dehider by nucular
(16) FPS Gauge by mniip
(17) TPT Radio by jward212
(18) Print Debugger by FeynmanLogomaker
(19) Powered BHOL by jacob1
(20) Light and lamps by electronic_steve
(21) Extremely Durable TTAN by QuentinADay
(22) New Buttons by QuentinADay
(23) Pure Energy by QuentinADay
(25) RCA's HUD XV Update I by RCAProduction
(27) 123456787654 vols by mjpowder
(28) Singularity Bomb by QuentinADay
(29) ES wifi set by electronic_steve
(30) Lua Elements Pack by FeynmanLogomaker
(31) stkm gun by jward212
(32) space building materials by kjack1111
(34) Rust bomb by Damian97
(35) Simple command block by ssccsscc
(36) Everlasting Fusion by QuentinADay
(37) Screenshot Organiser by mecha-man
(38) Napalm mod by cccp3
(39) Rocket fuel mod v0.15 by cccp3
(41) Useful web links by jward212
(42) TPT Logic Gates Mod by iamdumb
(43) ESTools by electronic_steve
(44) Head Crabs-HL2 by jward212
(45) Procedural Save Generator by boxmein
(46) smooth colours for nametag by jward212
(47) ZAKPACK by zak03
(49) Performance Monitor by FeynmanLogomaker
(51) Texter by byzod
(52) Texter default fonts by byzod
(53) Schicko's Font Pack for Texter by Schicko
(54) Realistic Element Names by Atomic10
(55) TPT's Mod V.3 Update 1 by Amy
(56) Temporaryaccount-Decorator by Temporaryaccount
(57) random save loader by jward212
(58) Tmp gradient display by ssccsscc
(59) particle re-orderer by mniip
(60) Electric Glow by jacob1
(61) More Fuel Mod-lite by jward212
(63) Rythidium by janekbe04
(64) Simple FPS GUI by Sfsjunior
(65) Enhanced Element Dehider by ChargedCreeper
(66) Graph of average temp by ssccsscc
(67) Template save loader by jacob1
(68) Lua Text Generator by jBot-42
(70) Pixel's Freezer by Pixelguru26
(71) Thingy | Fusion For Ever by TheChosenEvilOne
(72) op explosions by zolly_bro
(73) Scar by DorkyBobster
(74) ScreenShotMod by lill74
(77) Useful Things by TheEvilChosenOne
(78) Alchemy Mod by _MrN_
(79) Nuke v2 by Fnaf65
(80) Compressor mod by TheChosenEvilOne
(81) Custom Render Mode Loader by jacob1
(82) Spacewars by JosephMA
(83) MOAR - Alpha 0.1 by TheChosenEvilOne
(86) Element Creator by cxi
(88) Soapworm by LBPHacker
(90) Pressure Bomb by God_Kra
(91) SMFB by wntjq69
(92) Potato by cxi
(93) Subatomic Pack (BDS) by TPT_PL
(94) Acidic Pack (BDS) by TPT_PL
(95) Starbound Building Materials by Sanpypr
(96) Factory problems by TPT_PL
(97) Gamma Ray-diation by Kostia4381
(98) Magic by livingfossil
(99) Cross-window Copy/Cut/Paste by LBPHacker
(100) Langton's Ant with variations by LBPHacker
(101) Remote particle creator/deleter by TPT_PL
(102) Force fields by electronic_steve
(103) Reinforced Concrete by 12Me21
(105) TPT_PL's Lua Mod by TPT_PL
(106) Nuke v4 by Fnaf65
(107) CHEMMOD V1 by KevAK
(108) Chemicals by Ligan
(109) VonDaniel's Template by VonDaniel
(110) The Inaccurate Radioactivity Toy Mod by TuDoR2007
(112) textmonsterPack by textmonster404
(113) Meteor by TheScienceKid
(114) Tgpm by TuDoR2007
(115) Civilizations by TPT_PL
(116) RAD-MOD 1.2.1B by Kev_AK
(117) MicroLua by RamiLego4Game
(118) Extra customizable HUD by djy0212
(119) Ingame brush editor by ssccsscc
(120) Window Maker by Paul_31415
(121) CHEM-MOD V1.2B by Kev_AK
(122) Rainbow PHOT by Mrprocom
(123) stronger stickmanv by yuval
(124) 3D Pressure Visualizer by mniip
(125) Arkadian Liquid by JanKaszanka
(126) Fuel by nukers473
(127) Immersive Radioactivity v2.1 by Potbelly
(128) ElementLaunchingTool by juh9870
(129) CHEM-MOD_v1.2.2b by KevAK
(130) Slingshot by Mrprocom
(131) Perlin Noise Generator by DoubleF
(132) Element Replace by TomW1605
(133) Flooder V2 by TheAwesomeMutant
(134) Link Sign GUI by QuanTech
(135) Element dehider by 4e616d65
(136) Subphoton ROM Builder by mad-cow
(137) Hardened Dust by Liftski
(138) Bio-Vir by TheAwesomeMutant
(140) Orbit Simulator by Mrprocom
(141) johnnyou's Font for Texter by johnnyou (49796346)
(142) auto_wifi by phisically
(143) Layering helper by ssccsscc
(144) Layering Helper Extended by LuaMaster
(145) TPT Remade by TuDoR2007
(146) All-seeing sampler by djy0212
(147) Layering helper remastered by ssccsscc
(148) Eraser by thepowdertoy12
(149) EXPLOSIONS by olix3001
(150) Simple rocket fuel mod by ArseniyPlotnikov2006
(151) Pure Fission by Fnaf65
(154) Graph by ssccsscc
(155) Little's Pack! by LittleProgramming
(156) Lead by LoftisGaming
(157) WIFI Tuner by ssccsscc
(158) Previous Brush by TomW1605
(159) HUD Auto-Hider by Tim
(161) Stack tool by thepowdertoy12
(162) Oil and plastic by ArseniyPlotnikov2006
(163) Colored Ember by DUC
(164) Timer by ssccsscc
(165) Bacteria Mod by TuDoR2007
(166) Noise filter by LBPHacker
(167) Future-proof element dehider by LBPHacker
(168) RadioactiveNuke by DreamingWarlord
(169) Only Hot Element by DreamingWarlord
(170) Philosopher's Stone by Godhydra
(171) Conic section generator by LBPHacker
(172) Interface API by ssccsscc
(173) Metals&Materials by Ferrous26
(174) tpt.all by LBPHacker
(175) The Visual Elements Pack by Goblin01, vvv331
(176) Layering Helper Reforged by PowderNotSolid
(177) SNOWified SING by LBPHacker
(178) FPS Chart by Goblin01
(179) TPT font writer by Goblin01
(180) Simple Ruler by PowderNotSolid
(181) Heat Modifier by DreamingWarlord
(182) TPT Remade II by TuDoR2007
(183) Gravity simulator by ArseniyPlotnikov2k6
(184) Unobtainium by christheboss894
(185) TPTMIDI noteblock in tpt by djy0212
(186) DreamingWarlord's Lua Tool by DreamingWarlord
(187) Elements Tooltip by Goblin01
(189) Explodium script by 0d15ea5ebe57c0debadc0ffee0ddf00d
(190) more powered force elements by 6nop6nop
(191) Yzaak1Scifire Modpack by Yzaak1Scifire
(194) Fluor and more modpack! by galaktor
(195) Hot Powder by lieve_blendi
(196) Star by TUANMINHVIETNAM
(197) Heat Powders by lieve_blendi
(200) Tangeriinium (thx 2 cxi 4 code) by LostEditor
(201) Freezer by lieve_blendi
(202) Powder Power! by TPTSortaGuy
(203) PowderPlus v1.4 by PowderPlus Team
(204) fire by ME
(205) Stacked Goo Animations by Maticzpl
(206) Stickman Control for Android Version by PhauloRiquelme
(207) Spark Removal Button by Xyz
(208) More HEAC's! by Maxhd1234
(209) Immersive Radioactivity v3.0 by Potbelly
(210) Subframe Chipmaker Script by Maticzpl
(211) Realistic Propellants by ArseniyPlotnikov2k6
(212) Mass Equals Gravity by Maticzpl
(213) PhiMod v1 by ArolaunTech
(214) PC Controls for Android by Cracker1000
(217) Single-pixel pipe configurator by LBPHacker
(218) Omega Death Laser Gun by Dogeoum
(219) Notifications by Maticzpl
(221) Powderizer by ArolaunTech
(222) ElemDehider 1.2 by Inventor70
(223) Unobtainum V2 by DoomKittyAttack
(224) Organics Mod v0.2B by PowderPlus Team
(225) Gravity distortion by Avolte55
(226) tmp Wifi by PhauloRiquelme
(227) Alchemagica Mod v1.0 by RebMiami
(228) Fan Elements Mod by RebMiami
(229) Impossibilities by ArolaunTech
(230) Realistic Explosives by ArseniyPlotnikov2k6
(231) libactivation by anamorphic
(232) Alloy Brushes by Maticzpl
(233) Gravity bender by pres
(234) Slow Tick by Pixel
(235) Paste ID by Maticzpl
(236) many things by jadenflp2
(237) Territect by Rebmiami
(238) Better Descriptions v1.0.5 by ashyboi2022
(239) LIGHTNING SPRK by GOLmaster10101
(240) Small Bombs by juh9870
(241) Save Shop by aaccbb
(242) Moving solids v1.3.0 Beta by ArolaunTech
(244) Alchemistry by rdococ
(245) ETRD (Formerly PowderIM) by aaccbb
(246) RadonX by Justadirtblock
(248) Water-X by deuterium_oxide
(250) Indestructible INSL by CheekyRand0m
(252) Console's Mod by Console/Compec
(255) Slow motion by LBPHacker
(256) Powered Repeller by Hythonia
(257) Zeta's Electric Tools. by Zetalasis
(258) Azure serum (AZSR) by ALumpOfPowderToy
(259) COLORFULSAND by xert
(260) Lightning Circle by defaultuser0
(261) Powder Future Tech by JonaHungary
(262) TPTGlowingSolids by DestinyDyson
(263) Volcano Bomb by I_am_the_NugsWorld
(264) Neon Lights by Rebmiami
(265) Radioactive Materials by xyz
(266) Eater mod by VIPERGAMEZ
(267) the biology mod by someone
(268) Atomic Physics by qe
(269) Pure Radiation by ronansb
(270) Fake Elements by That_PowderToy_Guy
(271) Tachyons and MISC by RamenNoods
(272) Exotic Particles by rdococ
(273) FPS Slider by aaccbb
(274) Enphosian's Radioactive mod pack by Enphosian
(276) ROM Builder by QnpfvTPz
(277) acb's Idea Generator by aaccbb
(278) Bode's Grapher by Bodester
(279) Gradient Tool by Bodester
(280) Better FPS Slider by ngmoco
(281) MyWIFI by Bodester
(282) acb's Slow-Mo Script by aaccbb
(283) Fun n' Chemicals by 0xHenryMC
(284) Resistant coating by git4rker
(285) Nationwide script by Kit237
(286) weird alphabet.lua by alice_loona_ot12
(287) MT's font for texter by Kit237
(288) Compiler for the SCE computer by NoVIcE by NoVIcE
(289) Perlin Noise Map Generator by Kit236
(290) hell fire element by Hecker
(291) Power stuff by William
(292) Missile by BaCOn
(293) Wargame's things by Kit236
(294) Inert Gas by EwguhMicBewguhGuy
(295) Average Temperature Display by tptQuantification
(296) Radicons by stillthere
(297) ROCKET by stillthere
(298) NUUKE! by stillthere
(299) SubpixelScope by Rebmiami
(300) Unobtanium Element Pack by AlgoPowdo
(301) Magnets by TheSuperNova
(302) library007 2.0 3000 by 008cff
(316) TPTASM: Universal assembler for TPT computers by LBPHacker

+ Submit new script!

Title: Author:
Script:
_G.require = (function()
	local lineinfo = {
{ line = 133, mod_name = "tptasm" },
{ line = 320, mod_name = "tptasm.archs" },
{ line = 366, mod_name = "tptasm.archs.a728d28" },
{ line = 555, mod_name = "tptasm.archs.armatoste" },
{ line = 848, mod_name = "tptasm.archs.b29k1qs60" },
{ line = 996, mod_name = "tptasm.archs.hacks" },
{ line = 1076, mod_name = "tptasm.archs.i8m7d28s" },
{ line = 1312, mod_name = "tptasm.archs.maps" },
{ line = 1521, mod_name = "tptasm.archs.micro21" },
{ line = 1764, mod_name = "tptasm.archs.ptp7" },
{ line = 1922, mod_name = "tptasm.archs.r2" },
{ line = 2332, mod_name = "tptasm.archs.r3" },
{ line = 2689, mod_name = "tptasm.config" },
{ line = 2723, mod_name = "tptasm.detect" },
{ line = 2981, mod_name = "tptasm.emit" },
{ line = 3050, mod_name = "tptasm.evaluate" },
{ line = 3226, mod_name = "tptasm.opcode" },
{ line = 3290, mod_name = "tptasm.preprocess" },
{ line = 3825, mod_name = "tptasm.printf" },
{ line = 3901, mod_name = "tptasm.resolve" },
{ line = 4468, mod_name = "tptasm.tokenise" },
{ line = 4672, mod_name = "tptasm.utility" },
{ line = 4775, mod_name = "tptasm.xbit32" }
	}

	local unpack = rawget(_G, "unpack") or table.unpack
	local function packn(...)
		return { [ 0 ] = select("#", ...), ... }
	end
	local function unpackn(tbl, from, to)
		return unpack(tbl, from or 1, to or tbl[0])
	end
	local chunkname = debug.getinfo(1, "S").source
	if chunkname:find("^[=@]") then
		chunkname = chunkname:sub(2)
	else
		chunkname = nil
	end
	local function xpcall_wrap(func, handler)
		return function(...)
			local function escape_regex(str)
				return (str:gsub("[%$%%%(%)%*%+%-%.%?%]%[%^]", "%%%1"))
			end
			local function demangle(str)
				if chunkname then
					return str:gsub(escape_regex(chunkname) .. ":(%d+):", function(line)
						line = tonumber(line)
						for i = #lineinfo, 1, -1 do
							if lineinfo[i].line <= line then
								return ("%s$%s:%i:"):format(chunkname, lineinfo[i].mod_name, line - lineinfo[i].line + 1)
							end
						end
					end)
				end
				return str
			end
			local iargs = packn(...)
			local oargs
			xpcall(function()
				oargs = packn(func(unpackn(iargs)))
			end, function(err)
				if type(err) == "string" then
					err = demangle(err)
				end
				if handler then
					handler(err)
				end
				if type(err) == "string" then
					print(demangle(debug.traceback(err, 2)))
				end
				return err
			end)
			if oargs then
				return unpackn(oargs)
			end
		end
	end

	local real_require = _G.require
	local mod_func = {}
	local mod_state = {}
	local mod_result = {}
	local finalized = false

	local function temp_require(verb, reg_mod_name, reg_func)
		if not finalized then
			if verb == "run" then
				finalized = true
				_G.require = real_require
				return xpcall_wrap(temp_require(reg_mod_name).run)()
			elseif verb == "getenv" then
				local env = setmetatable({}, { __index = function(_, key)
					error("__index on env: " .. tostring(key), 2)
				end, __newindex = function(_, key)
					error("__newindex on env: " .. tostring(key), 2)
				end })
				for key, value in pairs(_G) do
					rawset(env, key, value)
				end
				rawset(env, "require", temp_require)
				rawset(env, "xpcall_wrap", xpcall_wrap)
				return env
			elseif verb == "register" then
				mod_func[reg_mod_name] = reg_func
			else
				error("bad verb")
			end
			return
		end
		local mod_name = verb
		if mod_state[mod_name] ~= "loaded" then
			if mod_state[mod_name] == "loading" then
				error("circular dependency", 2)
			end
			mod_state[mod_name] = "loading"
			mod_result[mod_name] = xpcall_wrap(mod_func[mod_name])()
			mod_state[mod_name] = "loaded"
		end
		return mod_result[mod_name]
	end
	return temp_require
end)()

if rawget(_G, "setfenv") then
	setfenv(1, require("getenv"))
else
	_ENV = require("getenv")
end

require("register", "tptasm", function()
local printf  = require("tptasm.printf")
local utility = require("tptasm.utility")

local function main(...)
	local exit_with = 0

	printf.update_colour()
	local old_print = print
	function print(...)
		printf.debug(utility.get_line(2), ...)
	end

	local args = { ... }
	xpcall_wrap(function()

		local detect = require("tptasm.detect")
		local archs = require("tptasm.archs")
		local preprocess = require("tptasm.preprocess")
		local emit = require("tptasm.emit")
		local resolve = require("tptasm.resolve")
		local xbit32 = require("tptasm.xbit32")

		local named_args, unnamed_args = utility.parse_args(args)

		local log_path = named_args.log or unnamed_args[3]
		if log_path then
			printf.redirect(log_path)
		end

		if type(named_args.anchor) == "string" then
			detect.make_anchor(named_args.anchor, named_args.anchor_dx, named_args.anchor_dy, named_args.anchor_prop, named_args.anchor_id)
			return
		end

		if named_args.silent then
			printf.silent = true
		end

		if named_args.detect then
			printf.info("listing targets")
			local counter = 0
			for x, y, model, id in detect.all_cpus() do
				printf.info(" * %s with ID %i at (%i, %i)", model, id, x, y)
				counter = counter + 1
			end
			printf.info("found %s targets", counter)
			return
		end

		local target = named_args.target or unnamed_args[2]
		local model_name = named_args.model or unnamed_args[4]

		if not model_name then
			model_name = detect.model(type(target) == "number" and target)
		end
		if not model_name then
			printf.failf("failed to detect model and no model name was passed")
		end

		local architecture_name = archs.get_name(model_name) or printf.failf("no architecture description for model '%s'", model_name)
		local architecture = archs.get_description(architecture_name)

		local root_source_path = tostring(named_args.source or unnamed_args[1] or printf.failf("no source specified"))
		local lines = preprocess(architecture, root_source_path)
		local to_emit, labels, model_restrictions = resolve.instructions(architecture, lines)

		for path, restrictions in pairs(model_restrictions) do
			local path_ok = false
			local failing_restriction_tokens = {}
			for _, restriction in ipairs(restrictions) do
				if model_name:find("^" .. restriction.value:gsub("^\"(.*)\"$", "%1") .. "$") then
					path_ok = true
				else
					failing_restriction_tokens[restriction] = true
				end
			end
			if not path_ok then
				local report_with = named_args.allow_model_mismatch and printf.warn or printf.err
				report_with("%s: unit incompatible with target model", path)
				for token in pairs(failing_restriction_tokens) do
					token:blamef(printf.info, "target model doesn't match this pattern")
				end
			end
		end
		if printf.err_called then
			printf.failf("model restriction check failed, bailing")
		end

		if named_args.export_labels then
			local handle = io.open(named_args.export_labels, "w")
			if handle then
				local sorted_labels = {}
				for name, address in pairs(labels) do
					table.insert(sorted_labels, {
						name = name,
						address = tonumber(address)
					})
				end
				table.sort(sorted_labels, function(lhs, rhs)
					if lhs.address < rhs.address then return  true end
					if lhs.address > rhs.address then return false end
					if lhs.name    < rhs.name    then return  true end
					if lhs.name    > rhs.name    then return false end
					return false
				end)
				for _, label in ipairs(sorted_labels) do
					handle:write(("%s 0x%X\n"):format(label.name, label.address))
				end
				handle:close()
			else
				printf.warn("failed to open '%s' for writing, no labels exported", tostring(named_args.export_labels))
			end
		end
		local opcodes = emit(architecture, to_emit, labels)

		if type(target) == "table" then
			for ix, ix_opcode in pairs(opcodes) do
				target[ix] = ix_opcode
			end
		elseif type(target) == "string" then
			local buf = {}
			local _, first = next(opcodes)
			if first then
				local width = #first.dwords
				for ix, ix_opcode in pairs(opcodes) do
					for ix_dword = 1, width do
						local dword = ix_opcode.dwords[ix_dword]
						buf[ix * width + ix_dword] = string.char(
							xbit32.band(              dword     , 0xFF),
							xbit32.band(xbit32.rshift(dword,  8), 0xFF),
							xbit32.band(xbit32.rshift(dword, 16), 0xFF),
							xbit32.band(xbit32.rshift(dword, 24), 0xFF)
						)
					end
				end
			end
			local handle = io.open(target, "wb")
			if handle then
				handle:write(table.concat(buf))
				handle:close()
			else
				printf.warn("failed to open '%s' for writing, no opcodes written", target)
			end
		else
			architecture.flash(model_name, target, opcodes)
			if printf.err_called then
				printf.failf("flashing stage failed, bailing")
			end
		end

	end, function(err)

		if err == printf.failf then
			exit_with = 1
		else
			-- * Dang.
			printf.info("this is an assembler bug, tell LBPHacker!")
			printf.info("https://github.com/LBPHacker/tptasm")
			exit_with = 2
		end

	end)()

	printf.unredirect()
	printf.info("done")
	print = old_print
	if not _G.tpt then
		os.exit(exit_with)
	end
	return exit_with
end

local function run(...)
	if _G.tpt and select("#", ...) == 0 then
		_G.tptasm = xpcall_wrap(main)
		return
	end
	return main(...)
end

return {
	run = run,
}

end)

require("register", "tptasm.archs", function()
local known_archs = {
	[   "A728D28" ] = require("tptasm.archs.a728d28"  ),
	[ "B29K1QS60" ] = require("tptasm.archs.b29k1qs60"),
	[  "I8M7D28S" ] = require("tptasm.archs.i8m7d28s" ),
	[      "MAPS" ] = require("tptasm.archs.maps"     ),
	[   "MICRO21" ] = require("tptasm.archs.micro21"  ),
	[      "PTP7" ] = require("tptasm.archs.ptp7"     ),
	[        "R2" ] = require("tptasm.archs.r2"       ),
	[        "R3" ] = require("tptasm.archs.r3"       ),
	[ "Armatoste" ] = require("tptasm.archs.armatoste"),
}
local function get_description(architecture_name)
	return known_archs[architecture_name]
end

local known_models_to_archs = {
	[  "A728D280" ] = "A728D28",   -- * "A7-28D28 Microcomputer" by Sam_Hayzen, id:2460726
	[  "A728D28A" ] = "A728D28",   -- * "A7-28D28 Microcomputer" by Sam_Hayzen, id:2460726
	[ "B29K1QS60" ] = "B29K1QS60", -- * "B29K1QS60" by unnick, id:2435570
	[  "I8M7D28S" ] = "I8M7D28S",  -- * "Guardian I8M7D28S" by Sam_Hayzen, id:2473628
	[      "MAPS" ] = "MAPS",      -- * "Computer (mapS)" by drakide, id:975033
	[   "MICRO21" ] = "MICRO21",   -- * "Micro Computer v2.1" by RockerM4NHUN, id:1599945
	[     "PTP7A" ] = "PTP7",      -- * "PTP7" by unnick, id:2458644
	[   "R216K2A" ] = "R2",        -- * "R216K2A" by LBPHacker, id:2303519
	[   "R216K4A" ] = "R2",        -- * "R216K4A" by LBPHacker, id:2305835
	[   "R216K8B" ] = "R2",        -- * "R216K8B" by LBPHacker, id:2342633
	[ "Armatoste" ] = "Armatoste", -- * yet unreleased architecture by DanielUbTb
}
for core_count = 1, 99 do
	for memory_rows = 1, 64 do
		known_models_to_archs[("R3A%02i%02i"):format(memory_rows, core_count)] = "R3" -- * yet unreleased architecture by LBPHacker
	end
end

local function get_name(model_name)
	return known_models_to_archs[model_name]
end

return {
	get_description = get_description,
	get_name = get_name,
}

end)

require("register", "tptasm.archs.a728d28", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")
local xbit32 = require("tptasm.xbit32")
local hacks  = require("tptasm.archs.hacks")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_

		%define dw `dw'
		%define org `org'

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local entities = {
	["r0"] = { type = "register", offset = 0 },
	["r1"] = { type = "register", offset = 1 },
	["r2"] = { type = "register", offset = 2 },
	["r3"] = { type = "register", offset = 3 },
}

local mnemonics = {
	[  "ldi"] = { class = "1I", bit =  0, flags = " s n " }, -- * r: write to and lock RP
	[   "ld"] = { class = "1R", bit =  3, flags = "r    " }, -- * s: redirect explicit branch ptr to P
	[   "st"] = { class = "1R", bit =  4, flags = "r    " }, -- * b: write to and lock EBP^
	[  "stl"] = { class = "0" , bit =  5, flags = "     " }, -- * n: write to and lock NIP
	["andrl"] = { class = "0" , bit =  6, flags = "     " }, -- * p: write to and lock P
	[  "xor"] = { class = "0" , bit =  7, flags = "     " },
	[   "rr"] = { class = "0" , bit =  8, flags = "     " },
	[ "setc"] = { class = "0" , bit =  9, flags = "     " },
	[  "brc"] = { class = "1I", bit = 10, flags = " s n " },
	[ "brnc"] = { class = "1I", bit = 10, flags = "    p" },
	[  "out"] = { class = "0" , bit = 11, flags = "     " },
	[ "bsto"] = { class = "0" , bit = 12, flags = "     " },
	[  "ldb"] = { class = "0" , bit = 13, flags = " s   " },
	[   "br"] = { class = "1I", bit = -1, flags = "  b  " },
}

local nop = opcode.make(28)

local dw_bits = 29

local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token_hax, parameters_hax, offset)
	local sub_instructions = hacks.pig(mnemonic_token_hax, parameters_hax, mnemonics)
	if not sub_instructions then
		return false
	end

	local final_code = nop:clone()
	local bit_invoked_by = {}
	local rp_locked = false
	local rp_value = 0
	local nip_locked = false
	local nip_value = 0
	local p_locked = false
	local p_value = 0
	local explicit_branch = false
	local branch_to_p = false
	for _, subinst in ipairs(sub_instructions) do
		if subinst.instr_desc.flags:find("s") then
			branch_to_p = true
		end
	end
	for _, subinst in ipairs(sub_instructions) do
		local bit = subinst.instr_desc.bit
		if bit_invoked_by[bit] then
			subinst.mnemonic_token:blamef(printf.err, "micro-instruction invoked multiple times")
			bit_invoked_by[bit]:blamef(printf.info, "first invoked here")
			return false
		end
		bit_invoked_by[bit] = subinst.mnemonic_token

		local imm
		if subinst.operand and subinst.operand:number() then
			imm = subinst.operand.parsed
			local trunc = 0x80
			if imm >= trunc then
				imm = imm % trunc
				subinst.operand:blamef(printf.warn, "number truncated to 7 bits")
			end
		end

		local temp_flags = subinst.instr_desc.flags
		if temp_flags:find("b") then
			explicit_branch = true
			if branch_to_p then
				temp_flags = temp_flags .. "p"
			else
				temp_flags = temp_flags .. "n"
			end
		end

		if temp_flags:find("p") then
			if p_locked and subinst.operand.parsed ~= p_value then
				subinst.mnemonic_token:blamef(printf.err, "general pointer already locked")
				p_locked:blamef(printf.info, "locked by this")
				return false
			end
			p_locked = subinst.mnemonic_token
			p_value = imm
		end

		if temp_flags:find("n") then
			if nip_locked and subinst.operand.parsed ~= nip_value then
				subinst.mnemonic_token:blamef(printf.err, "next instruction pointer already locked")
				nip_locked:blamef(printf.info, "locked by this")
				return false
			end
			nip_locked = subinst.mnemonic_token
			nip_value = imm
		end

		if temp_flags:find("r") then
			if rp_locked and subinst.operand.entity.offset ~= rp_value then
				subinst.mnemonic_token:blamef(printf.err, "register bits already locked")
				rp_locked:blamef(printf.info, "locked by this")
				return false
			end
			rp_locked = subinst.mnemonic_token
			rp_value = subinst.operand.entity.offset
		end

		final_code:merge(1, bit)
	end
	if not explicit_branch then
		local next_inst = xbit32.band(offset + 1, 0x7F)
		if branch_to_p then
			p_value = next_inst
		else
			nip_value = next_inst
		end
	end
	final_code:merge(p_value, 21)
	final_code:merge(nip_value, 14)
	final_code:merge(rp_value, 1)

	return true, { final_code }
end

local mnemonics = {}
for key in pairs(mnemonics) do
	mnemonics[key] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local space_available = ({
		["A728D280"] =  0,
		["A728D28A"] = 16,
	})[model]
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end

	for ix = 0, #opcodes do
		sim.partProperty(sim.partID(x + 18 - ix, y - 9), "ctype", 0x20000000 + opcodes[ix].dwords[1])
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.armatoste", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_
		
		%define dw `dw'
		%define org `org'

		%macro mov OutRegis, InRegOrImm
			orr OutRegis, r0, InRegOrImm
		%endmacro

		%macro not OutRegis, In
			sub te, r0, 1
			xor outRegis, te, In
		%endmacro

		%macro nop
			orr r0, r0, r0
		%endmacro

		%macro jum Address
			jal r0, Address
		%endmacro

		%macro bls In1, In2, Address
			sls	te, In1, In2
			bne	te, r0, Address
		%endmacro

		%macro bge In1, In2, Address
			sls	te, In1, In2
			beq	te, r0, Address
		%endmacro

		%macro bgr In1, In2, Address
			sls	te, In2, In1
			bne	te, r0, Address
		%endmacro

		%macro ble In1, In2, Address
			sls	te, In2, In1
			beq	te, r0, Address
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local dw_bits = 29

local nop = opcode.make(32):merge(0x2C000000, 0)

local entities = {
	[  "r0" ] = { type = "register", offset =  0 },
	[  "r1" ] = { type = "register", offset =  1 },
	[  "r2" ] = { type = "register", offset =  2 },
	[  "r3" ] = { type = "register", offset =  3 },
	[  "r4" ] = { type = "register", offset =  4 },
	[  "r5" ] = { type = "register", offset =  5 },
	[  "r6" ] = { type = "register", offset =  6 },
	[  "r7" ] = { type = "register", offset =  7 },
	[  "r8" ] = { type = "register", offset =  8 },
	[  "r9" ] = { type = "register", offset =  9 },
	[ "r10" ] = { type = "register", offset = 10 },
	[ "r11" ] = { type = "register", offset = 11 },
	[ "r12" ] = { type = "register", offset = 12 },
	[ "r13" ] = { type = "register", offset = 13 },
	[ "r14" ] = { type = "register", offset = 14 },
	[ "r15" ] = { type = "register", offset = 15 },
}
entities["ra"] = entities["r10"]
entities["rb"] = entities["r11"]
entities["rc"] = entities["r12"]
entities["rd"] = entities["r13"]
entities["re"] = entities["r14"]
entities["rf"] = entities["r15"]
entities["ze"] = entities["r0"]
entities["te"] = entities["r13"]
entities["jl"] = entities["r14"]
entities["sp"] = entities["r15"]

local mnemonics = {}

local mnemonic_to_class_code = {
	[ "mju" ] = { class = " BX", code = 0x20000000 },
	[ "wmj" ] = { class = " BX", code = 0x20100000 },
	[ "jal" ] = { class = "A X", code = 0x21000000 },
	[ "beq" ] = { class = "ABX", code = 0x22000000 },
	[ "bne" ] = { class = "ABX", code = 0x23000000 },
	[ "loa" ] = { class = "ABX", code = 0x24000000 },
	[ "inn" ] = { class = "ABX", code = 0x25000000 },
	[ "sto" ] = { class = "ABX", code = 0x26000000 },
	[ "out" ] = { class = "ABX", code = 0x27000000 },
	[ "add" ] = { class = "ABX", code = 0x28000000 },
	[ "sub" ] = { class = "ABX", code = 0x29000000 },
	[ "lbl" ] = { class = "ABX", code = 0x2A000000 },
	[ "lbr" ] = { class = "ABX", code = 0x2B000000 },
	[ "and" ] = { class = "ABX", code = 0x2C000000 },
	[ "orr" ] = { class = "ABX", code = 0x2D000000 },
	[ "xor" ] = { class = "ABX", code = 0x2E000000 },
	[ "sls" ] = { class = "ABX", code = 0x2F000000 },
}

local mnemonic_desc = {}
function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 1
		   and ix_param[1]:is("entity") and ix_param[1].entity.type == "register" then
			table.insert(operands, {
				type = "reg",
				value = ix_param[1].entity.offset,
			})

		elseif #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1],
			})

		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local final_code
	local class_code = mnemonic_to_class_code[mnemonic_token.value]
	local code = opcode.make(32):merge(class_code.code, 0)
	local ok = true
	local function push(operand, shift)
		if operand.type == "reg" then
			code = code:merge(operand.value, shift)
		elseif shift == 0 and operand.type == "imm" then
			local width = 16
			local value = operand.value
			if value >= 2 ^ width then
				value = value % 2 ^ width
				operand.token:blamef(printf.warn, "number truncated to " .. width .. " bits")
			end
			code = code:merge(0x10000000, 0):merge(value, 0)
		else
			ok = false
		end
	end
	local index = 0
	if class_code.class:find("A") then
		index = index + 1
		push(operands[index], 20)
	end
	if class_code.class:find("B") then
		index = index + 1
		push(operands[index], 16)
	end
	if class_code.class:find("X") then
		index = index + 1
		push(operands[index],  0)
	end
	if index ~= #operands then
		ok = false
	end
	if ok then
		final_code = code
	end

	if not final_code then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end

	return true, { final_code }
end

for mnemonic in pairs(mnemonic_to_class_code) do
	mnemonics[mnemonic] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local function filt(column, row)
		return x + 1 + column, y + 7 - row
	end

	local ram_width = 128
	local max_ram_height_order = 6
	local space_needed = #opcodes + 1
	local ram_height_order

	local vertical_space
	local seen_empty = false
	for row = 0, 2 ^ max_ram_height_order - 1 do
		local filled = true
		local empty = true
		for column = 0, ram_width - 1 do
			local id = sim.partID(filt(column, row))
			if id then
				empty = false
				if sim.partProperty(id, "type") ~= elem.DEFAULT_PT_FILT then
					filled = false
				end
			end
		end
		if filled and not seen_empty or empty then
			vertical_space = row + 1
		else
			break
		end
		if empty then
			seen_empty = true
		end
	end

	for order = 0, max_ram_height_order do
		if space_needed <= ram_width * 2 ^ order then
			if vertical_space < 2 ^ order then
				printf.err("out of vertical space; code takes %i cells, planned to build %i rows of RAM, could only build %i", space_needed, 2 ^ order, vertical_space)
				return
			end
			ram_height_order = order
			break
		end
	end
	if not ram_height_order then
		local space_available = ram_width * 2 ^ max_ram_height_order
		printf.err("out of space; code takes %i cells, only have at most %i", space_needed, space_available)
		return
	end

	for row = 0, vertical_space - 1 do
		for column = 0, ram_width - 1 do
			local xx, yy = filt(column, row)
			local id = sim.partID(xx, yy)
			if row >= 2 ^ ram_height_order then
				if id then
					sim.partKill(id)
				end
			else
				if not id then
					id = sim.partCreate(-3, xx, yy, elem.DEFAULT_PT_FILT)
					if id == -1 then
						printf.err("out of particle IDs")
						return
					end
				end
				local index = row * ram_width + column
				local opcode = opcodes[index] and opcodes[index].dwords[1] or nop.dwords[1]
				sim.partProperty(id, "ctype", opcode)
			end
		end
	end

	sim.partProperty(sim.partID(x - 4, y + 1), "ctype", 0x10000000 + 2 ^ (ram_height_order + 1) - 1)
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.b29k1qs60", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_
		
		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local dw_bits = 29 -- * TODO: figure out how dw even works here

local nop = opcode.make(192)
	:merge(0x20000000,   0)
	:merge(0x20000000,  32)
	:merge(0x20000000,  64)
	:merge(0x20000000,  96)
	:merge(0x20000000, 128)
	:merge(0x20000000, 160)

local entities = {}

local jmem_mnemonics = {
	["mvaz"] = { code = 0x20000000, jump = false, mask = false },
	["mvjz"] = { code = 0x20000001, jump =  true, mask = false },
	["ldaz"] = { code = 0x20000002, jump = false, mask =  true },
	["ldjz"] = { code = 0x20000003, jump =  true, mask =  true },
	["staz"] = { code = 0x20000004, jump = false, mask = false },
	["stjz"] = { code = 0x20000005, jump =  true, mask = false },
	["exaz"] = { code = 0x20000006, jump = false, mask = false },
	["exjz"] = { code = 0x20000007, jump =  true, mask = false },
}

local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local parameter_ix = 0
	local function take_parameter(role)
		parameter_ix = parameter_ix + 1
		if not parameters[parameter_ix] then
			mnemonic_token:blamef(printf.err, "%s not specified", role)
			return false
		end
		local parameter = parameters[parameter_ix]
		if #parameter < 1 then
			parameter.before:blamef(printf.err, "no tokens in %s", role)
			return false
		end
		if #parameter > 1 then
			parameter[2]:blamef(printf.err, "excess tokens in %s", role)
			return false
		end
		if not parameter[1]:number() then
			parameter[1]:blamef(printf.err, "%s is not a number", role)
			return false
		end
		return parameter[1].parsed
	end

	local final_code = nop:clone()
	local instr_desc = jmem_mnemonics[mnemonic_token.value]
	final_code:merge(instr_desc.code, 128)
	do
		local set = take_parameter("bits to set")
		if not set then
			return false
		end
		final_code:merge(set, 0)
	end
	do
		local reset = take_parameter("bits to reset")
		if not reset then
			return false
		end
		final_code:merge(reset, 32)
	end
	if instr_desc.jump then
		do
			local condition = take_parameter("jump condition")
			if not condition then
				return false
			end
			final_code:merge(condition, 64)
		end
		do
			local target = take_parameter("jump target")
			if not target then
				return false
			end
			final_code:merge(target, 96)
		end
	end
	if instr_desc.mask and not instr_desc.jump then
		do
			local mask = take_parameter("read mask")
			if not mask then
				return false
			end
			final_code:merge(mask, 64)
		end
	end
	return true, { final_code }
end

local mnemonics = {}
for key in pairs(jmem_mnemonics) do
	mnemonics[key] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end
	local space_available = 0x100
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end
	for ix = 0, #opcodes do
		for iy = 1, 6 do
			sim.partProperty(sim.partID(x + ix, y + iy + 3), "ctype", opcodes[ix].dwords[iy])
		end
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.hacks", function()
local printf = require("tptasm.printf")

local function pig(mnemonic_token_hax, parameters_hax, mnemonics)
	local sub_instructions = {}

	local tokens = { mnemonic_token_hax }
	for _, parameter in ipairs(parameters_hax) do
		for _, token in ipairs(parameter) do
			table.insert(tokens, token)
		end
	end

	while tokens[1] do
		local mnemonic_token = tokens[1]
		table.remove(tokens, 1)

		local instr_desc = mnemonics[mnemonic_token.value]
		if not instr_desc then
			mnemonic_token:blamef(printf.err, "unknown mnemonic")
			return false
		end

		local wants_operands = tonumber(instr_desc.class:sub(1, 1))
		local operand
		do
			local operand_list = {}
			while tokens[1] and not tokens[1]:punctuator("|") do
				table.insert(operand_list, tokens[1])
				table.remove(tokens, 1)
			end
			if tokens[1] and tokens[1]:punctuator("|") then
				table.remove(tokens, 1)
			end

			if #operand_list > wants_operands then
				operand_list[wants_operands + 1]:blamef(printf.err, "excess operands")
				return false
			end
			if #operand_list < wants_operands then
				if #operand_list == 0 then
					mnemonic_token:blamef_after(printf.err, "insufficient operands")
				else
					operand_list[#operand_list]:blamef_after(printf.err, "insufficient operands")
				end
				return false
			end
			operand = operand_list[1]
		end

		if operand then
			local expect_class = "X"
			if operand:number() then
				expect_class = "I"
			end
			if operand:is("entity") then
				expect_class = "R"
			end
			if not instr_desc.class:find(expect_class) then -- * it can only be a register
				mnemonic_token:blamef(printf.err, "no variant of '%s' exists that takes a %s operand '%s'", mnemonic_token.value, operand.type, operand.value)
				return false
			end
		end

		table.insert(sub_instructions, {
			instr_desc = instr_desc,
			mnemonic_token = mnemonic_token,
			operand = operand
		})
	end

	return sub_instructions
end

return {
	pig = pig, -- * Aka piped instruction groups. (Stupid way to say that multiple instructions exist on the same source line and are delimited by '|'.)
}

end)

require("register", "tptasm.archs.i8m7d28s", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")
local xbit32 = require("tptasm.xbit32")
local hacks  = require("tptasm.archs.hacks")

local arch_i8m7d28s = {}

local local_config = {
	org_data_base = 0x100
}
local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_

		%define dw `dw'
		%define org `org'

		%define org_data_base `org_data_base'

		%macro org_data origin
			org { org_data_base origin + }
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap] or local_config[cap]
	end)
}

local entities = {
	-- * lol nothing
}

local mnemonics = {
	[   "nop"] = { class =  "0", value = 0x000, flags = "clam " }, -- * c: control slot
	[   "pop"] = { class =  "0", value = 0x200, flags = "   m " }, -- * l: logical slot
	[  "stsp"] = { class =  "0", value = 0x400, flags = "   m " }, -- * a: arithmetical slot
	[  "push"] = { class =  "0", value = 0x600, flags = "   m " }, -- * m: memory slot
	[   "ldm"] = { class = "1I", value = 0x800, flags = "   mp" }, -- * p: write to and lock P
	[    "ld"] = { class = "1I", value = 0x800, flags = "q  mp" }, -- * s: redirect explicit branch ptr to P
	["puship"] = { class =  "0", value = 0xA00, flags = "   m " }, -- * b: write to and lock EBP^
	[   "stm"] = { class = "1I", value = 0xC00, flags = "   mp" }, -- * n: write to and lock IP
	[    "st"] = { class = "1I", value = 0xC00, flags = "q  mp" }, -- * o: write to in/out 1-bit offset
	[ "pushi"] = { class = "1I", value = 0xE00, flags = "   mp" }, -- * q: subtract org_data_base from immediate
	[   "sta"] = { class =  "0", value = 0x040, flags = "  a  " },
	[   "dec"] = { class =  "0", value = 0x080, flags = "  a  " },
	[   "inc"] = { class =  "0", value = 0x0C0, flags = "  a  " },
	[  "subi"] = { class = "1I", value = 0x100, flags = "  a p" },
	[  "addi"] = { class = "1I", value = 0x140, flags = "  a p" },
	[   "sub"] = { class =  "0", value = 0x180, flags = "  a  " },
	[   "add"] = { class =  "0", value = 0x1C0, flags = "  a  " },
	[    "or"] = { class =  "0", value = 0x008, flags = " l   " },
	[   "and"] = { class =  "0", value = 0x010, flags = " l   " },
	[   "xor"] = { class =  "0", value = 0x018, flags = " l   " },
	[    "rr"] = { class =  "0", value = 0x020, flags = " l   " },
	[    "rl"] = { class =  "0", value = 0x028, flags = " l   " },
	[   "rng"] = { class =  "0", value = 0x030, flags = " l   " },
	[   "stl"] = { class =  "0", value = 0x038, flags = " l   " },
	[    "jp"] = { class = "1I", value = 0x000, flags = "  b  " },
	[   "jpb"] = { class =  "0", value = 0x001, flags = "c    " },
	[    "jz"] = { class = "1I", value = 0x002, flags = "c   p" },
	[    "jc"] = { class = "1I", value = 0x003, flags = "c   p" },
	[   "jnz"] = { class = "1I", value = 0x002, flags = "c ns " },
	[   "jnc"] = { class = "1I", value = 0x003, flags = "c ns " },
	[   "out"] = { class = "1I", value = 0x004, flags = "co   " },
	[    "in"] = { class = "1I", value = 0x005, flags = "co   " },
}

local nop = opcode.make(28):merge(0x20000000, 0)

local dw_bits = 29

local clam_slot_name = {
	["c"] = "control",
	["l"] = "logical",
	["a"] = "arithmetical",
	["m"] = "memory"
}

local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token_hax, parameters_hax, offset)
	local sub_instructions = hacks.pig(mnemonic_token_hax, parameters_hax, mnemonics)
	if not sub_instructions then
		return false
	end

	local final_code = nop:clone()
	local clam_slot_used = {}
	local ip_locked = false
	local ip_value = 0
	local p_locked = false
	local p_value = 0
	local explicit_branch = false
	local branch_to_p = false
	for _, subinst in ipairs(sub_instructions) do
		if subinst.instr_desc.flags:find("s") then
			branch_to_p = true
		end
	end
	for _, subinst in ipairs(sub_instructions) do
		for clam_slot in subinst.instr_desc.flags:gmatch("[clam]") do
			if clam_slot_used[clam_slot] then
				subinst.mnemonic_token:blamef(printf.err, "micro-instruction slot '%s' used multiple times", clam_slot_name[clam_slot])
				clam_slot_used[clam_slot]:blamef(printf.info, "first used here")
				return false
			end
			clam_slot_used[clam_slot] = subinst.mnemonic_token
		end

		local imm
		if subinst.operand and subinst.operand:number() then
			imm = subinst.operand.parsed
			local bits = subinst.instr_desc.flags:find("o") and 1 or 8
			local trunc = 2 ^ bits
			if subinst.instr_desc.flags:find("q") then
				imm = imm - local_config.org_data_base
			end
			if imm >= trunc then
				imm = imm % trunc
				subinst.operand:blamef(printf.warn, "number truncated to %i bits", bits)
			end
		end

		local temp_flags = subinst.instr_desc.flags
		if temp_flags:find("b") then
			explicit_branch = true
			if branch_to_p then
				temp_flags = temp_flags .. "p"
			else
				temp_flags = temp_flags .. "n"
			end
		end

		if temp_flags:find("p") then
			if p_locked and subinst.operand.parsed ~= p_value then
				subinst.mnemonic_token:blamef(printf.err, "general pointer already locked")
				p_locked:blamef(printf.info, "locked by this")
				return false
			end
			p_locked = subinst.mnemonic_token
			p_value = imm
		end

		if temp_flags:find("n") then
			if ip_locked and subinst.operand.parsed ~= ip_value then
				subinst.mnemonic_token:blamef(printf.err, "instruction pointer already locked")
				ip_locked:blamef(printf.info, "locked by this")
				return false
			end
			ip_locked = subinst.mnemonic_token
			ip_value = imm
		end

		if temp_flags:find("o") then
			final_code:merge(imm, 1)
		end

		final_code:merge(subinst.instr_desc.value, 0)
	end
	if not explicit_branch then
		local next_inst = xbit32.band(offset + 1, 0xFF)
		if branch_to_p then
			p_value = next_inst
		else
			ip_value = next_inst
		end
	end
	final_code:merge(p_value, 12)
	final_code:merge(ip_value, 20)

	return true, { final_code }
end

local mnemonics = {}
for key in pairs(mnemonics) do
	mnemonics[key] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local space_available = 0x180
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end

	do
		local frame_pos = -1
		while sim.partProperty(sim.partID(x + frame_pos, y + 3), "type") == elem.DEFAULT_PT_PSTN do
			frame_pos = frame_pos + 1
		end
		for ix = 0x00, 0xFF do
			local code = opcodes[ix] and opcodes[ix].dwords[1] or 0x20000000
			local column = ix % 32
			local row = (ix - column) / 32
			sim.partProperty(sim.partID(x + frame_pos + 2 + column, y + 5 + row), "ctype", code)
		end
	end
	do
		local frame_pos = -3
		while sim.partProperty(sim.partID(x + frame_pos, y + 50), "type") == elem.DEFAULT_PT_PSTN do
			frame_pos = frame_pos + 1
		end
		for ix = 0x00, 0x7F do
			local data = opcodes[ix + 0x100] and opcodes[ix + 0x100].dwords[1] or 0x20000000
			local column = ix % 16
			local row = (ix - column) / 16
			sim.partProperty(sim.partID(x + frame_pos + 2 + column, y + 57 + row), "ctype", data)
		end
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.maps", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")
local xbit32 = require("tptasm.xbit32")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_

		%define dw `dw'
		%define org `org'

		%macro jz Label
			jnzg . `peerlabel' _Jz `macrounique', Label
		. `peerlabel' _Jz `macrounique':
		%endmacro

		%macro jzg Label, GLabel
			jnzg GLabel, Label
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local entities = {}
local nop = opcode.make(18)
local dw_bits = 18

local mnemonic_to_class_code_g = {
	[  "st" ] = { class = "S1", code = 0x00000 },
	[  "gt" ] = { class =  "1", code = 0x00280 },
	[  "lt" ] = { class =  "1", code = 0x00200 },
	[ "jnz" ] = { class = "C1", code = 0x00400 },
	[  "ld" ] = { class =  "1", code = 0x00680 },
	[ "xor" ] = { class =  "1", code = 0x01080 },
	[ "add" ] = { class =  "1", code = 0x01280 },
	[ "and" ] = { class =  "1", code = 0x01400 },
	[  "or" ] = { class =  "1", code = 0x01680 },
	[ "shl" ] = { class =  "0", code = 0x00900 },
	[ "shr" ] = { class =  "0", code = 0x00B00 },
}
local mnemonic_to_class_code = {}
for mnemonic, class_code in pairs(mnemonic_to_class_code_g) do
	mnemonic_to_class_code[mnemonic       ] = { class = class_code.class       , code = class_code.code }
	mnemonic_to_class_code[mnemonic .. "g"] = { class = class_code.class .. "G", code = class_code.code }
end
local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters, offset)
	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 3
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:number()
		   and ix_param[3]:punctuator("]") then
			table.insert(operands, {
				type = "mem",
				value = ix_param[2].parsed,
				token = ix_param[2]
			})

		elseif #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1]
			})
		   
		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local desc = mnemonic_to_class_code[mnemonic_token.value]
	local operand_mode_valid = false
	local wants_operands = (desc.class:find("1") and 1 or 0) + (desc.class:find("G") and 1 or 0)
	if #operands == wants_operands then
		operand_mode_valid = true
	end
	if wants_operands == 1 and desc.class:find("C") and operands[1].type ~= "imm" then
		operand_mode_valid = false
	end
	if not operand_mode_valid then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end

	local goto_target = offset + 1
	local imm_operand = 0
	if desc.class:find("G") then
		local operand = operands[#operands]
		local truncated = operand.value % 0x80
		local value = operand.value
		if value >= 0x80 then
			value = value % 0x80
			operand.token:blamef(printf.warn, "number truncated to 7 bits")
		end
		goto_target = value
		operands[#operands] = nil
	end
	if desc.class:find("1") then
		local limit = desc.class:find("C") and 7 or 5
		local operand = operands[#operands]
		local truncated = operand.value % 2 ^ limit
		local value = operand.value
		if value >= 2 ^ limit then
			value = value % 2 ^ limit
			operand.token:blamef(printf.warn, "number truncated to " .. limit .. " bits")
		end
		imm_operand = value
		if operand.type == "mem" and imm_operand >= 0x10 and not desc.class:find("S") then
			imm_operand = imm_operand % 2 * 2 + 0x10
		end
		if operand.type == "imm" and not desc.class:find("C") then
			imm_operand = xbit32.band(xbit32.rshift(imm_operand, 4), 1) + xbit32.band(xbit32.rshift(imm_operand, 2), 2) + xbit32.lshift(xbit32.band(imm_operand, 3), 3) + xbit32.band(imm_operand, 4) + 0x20
		end
	end

	local final_code = opcode.make(18):merge(desc.code, 0)
	if desc.class:find("C") then
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 6), 1),  7)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 5), 1), 13)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 4), 1), 11)
		final_code:merge(xbit32.band(              imm_operand    , 1), 15)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 1), 1), 17)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 2), 1), 16)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 3), 1), 14)
	else
		final_code:merge(xbit32.band(              imm_operand    , 15), 14)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 4),  1), 13)
		final_code:merge(xbit32.band(xbit32.rshift(imm_operand, 5),  1), 11)
	end
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 5), 1), 0)
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 4), 1), 1)
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 1), 1), 2)
	final_code:merge(xbit32.band(              goto_target    , 1), 3)
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 2), 1), 4)
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 6), 1), 5)
	final_code:merge(xbit32.band(xbit32.rshift(goto_target, 3), 1), 6)

	return true, { final_code }
end

local mnemonics = {}
for key in pairs(mnemonic_to_class_code) do
	mnemonics[key] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local space_available = 0x80
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end

	for ix = 0, 127 do
		local opcode = opcodes[ix] and opcodes[ix].dwords[1] or 0x00E00
		local x_ = x + bit.band(bit.rshift(ix, 6), 1)
		local y_ = y + 1 + bit.band(ix, 63)
		for ib = 0, 17 do
			local old = sim.partID(x_ + ib * 2, y_)
			if old then
				sim.partKill(old)
			end
			if bit.band(opcode, bit.lshift(1, ib)) ~= 0 then
				local new = sim.partCreate(-3, x_ + ib * 2, y_, elem.DEFAULT_PT_FILT)
				sim.partProperty(new, "ctype", 0x20000001)
			end
		end
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.micro21", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")
local xbit32 = require("tptasm.xbit32")

local macros_str
local macros_arr = {}
for macro, code in pairs({
	[  "c"] = "if  s,  1",
	[  "a"] = "if  s,  2",
	[  "e"] = "if  s,  4",
	[  "b"] = "ifn s,  6",
	[ "2z"] = "if  s,  8",
	[ "1z"] = "if  s, 16",
	[ "ly"] = "if  s, 32",
	[ "nc"] = "ifn s,  1",
	[ "na"] = "ifn s,  2",
	[ "ne"] = "ifn s,  4",
	[ "nb"] = "if  s,  6",
	["n2z"] = "ifn s,  8",
	["n1z"] = "ifn s, 16",
	[ "ln"] = "ifn s, 32",
}) do
	table.insert(macros_arr, ([==[
		%%macro if%s
			%s
		%%endmacro
	]==]):format(macro, code))
	table.insert(macros_arr, ([==[
		%%macro j%s loc
			if%s
			jmp loc
		%%endmacro
	]==]):format(macro, macro))
end
macros_str = table.concat(macros_arr)

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_

		%define dw `dw'
		%define org `org'

		%macro ifz reg
			ifn reg, 0xFF
		%endmacro

		%macro ifnz reg
			if reg, 0xFF
		%endmacro
	]==] .. macros_str .. [==[

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local dw_bits = 17

local nop = opcode.make(17)

local entities = {
	["a"] = { type = "register", offset = 1 },
	["b"] = { type = "register", offset = 2 },
	["s"] = { type = "register", offset = 3 },
}

local mnemonics = {}
local mnemonic_to_class_code = {
	[ "stop"] = { class = "nop", code = 0x00000 },
	["stopv"] = { class = "nop", code = 0x00FFF },
	[  "jmp"] = { class =   "0", code = 0x01000 },
	[   "if"] = { class =  "01", code = 0x02000 },
	[  "ifn"] = { class =  "01", code = 0x03000 },
	[  "nin"] = { class =   "0", code = 0x04000 },
	[ "copy"] = { class =  "01", code = 0x05000 },
	[  "lin"] = { class = "nop", code = 0x06000 },
	[  "rnd"] = { class = "nop", code = 0x07000 },
	[  "adc"] = { class =  "01", code = 0x08000 },
	[  "add"] = { class =  "01", code = 0x09000 },
	[  "lod"] = { class =  "01", code = 0x0A000 },
	[  "sto"] = { class =  "01", code = 0x0B000 },
	[  "and"] = { class =  "01", code = 0x0C000 },
	[   "or"] = { class =  "01", code = 0x0D000 },
	[  "xor"] = { class =  "01", code = 0x0E000 },
	[  "shr"] = { class =  "01", code = 0x10000 },
	[  "shl"] = { class =  "01", code = 0x11000 },
	[  "rtr"] = { class =  "01", code = 0x12000 },
	[  "rtl"] = { class =  "01", code = 0x13000 },
	[  "out"] = { class =  "0?", code = 0x14000 },
	[ "stip"] = { class = "nop", code = 0x15000 },
	[ "test"] = { class =  "01", code = 0x16000 },
	[ "sysr"] = { class = "nop", code = 0x17000 },
	[  "neg"] = { class =  "01", code = 0x18000 },
	[  "nop"] = { class = "nop", code = 0x1F000 },
	[ "nopv"] = { class = "nop", code = 0x1FFFF },
}

local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 1
		   and ix_param[1]:is("entity") and ix_param[1].entity.type == "register" then
			table.insert(operands, {
				type = "reg",
				value = ix_param[1].entity.offset
			})

		elseif #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1]
			})
		   
		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local desc = mnemonic_to_class_code[mnemonic_token.value]
	local operand_mode_valid = false
	if (desc.class == "nop" and #operands == 0)
	or ((desc.class == "0" or desc.class == "0?") and #operands == 1)
	or ((desc.class == "01" or desc.class == "0?") and #operands == 2) then
		operand_mode_valid = true
	end
	if #operands == 2 and operands[1].type == "imm" and operands[2].type == "imm" then
		operand_mode_valid = false
	end
	if not operand_mode_valid then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end

	local final_code = opcode.make(17):merge(desc.code, 0)
	local function bind_operand(slot, operand)
		if operand.type == "imm" then
			local truncated = operand.value % 0x100
			local value = operand.value
			if value >= 0x100 then
				value = value % 0x100
				operands[ix].token:blamef(printf.warn, "number truncated to 8 bits")
			end
			final_code:merge(value, 0)
		elseif operand.type == "reg" then
			final_code:merge(operand.value, 12 - slot * 2)
		end
	end
	for ix, ix_op in ipairs(operands) do
		bind_operand(ix, ix_op)
	end
	return true, { final_code }
end
for mnemonic in pairs(mnemonic_to_class_code) do
	mnemonics[mnemonic] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end
	local space_available = 0x80
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end
	local colour_codes = {
		[ 0] = 0xFF00FF00,
		[ 1] = 0xFF00FF00,
		[ 2] = 0xFF00FF00,
		[ 3] = 0xFF00FF00,
		[ 4] = 0xFF00FF00,
		[ 5] = 0xFF00FF00,
		[ 6] = 0xFF00FF00,
		[ 7] = 0xFF00FF00,
		[ 8] = 0xFFFF00FF,
		[ 9] = 0xFFFF00FF,
		[10] = 0xFFFFFF00,
		[11] = 0xFFFFFF00,
		[12] = 0xFFFF0000,
		[13] = 0xFFFF0000,
		[14] = 0xFFFF0000,
		[15] = 0xFFFF0000,
		[16] = 0xFFFF0000,
	}
	for iy = 0, 127 do
		local code = opcodes[iy] and opcodes[iy].dwords[1] or 0
		for ix = 0, 16 do
			local px, py = x - ix + 354, y + iy + 73
			local old_id = sim.partID(px, py)
			if old_id then
				sim.partKill(old_id)
			end
			if xbit32.band(code, 2 ^ ix) ~= 0 then
				local new_id = sim.partCreate(-2, px, py, elem.DEFAULT_PT_ARAY)
				local colour_code = colour_codes[ix]
				if code == 0x1FFFF or code == 0x00FFF then
					colour_code = 0xFF00FFFF
				end
				if xbit32.band(code, 0x1F000) == 0x01000 and ix < 8 then
					colour_code = 0xFF00FFFF
				end
				sim.partProperty(new_id, "dcolour", colour_code)
			end
		end
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.ptp7", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_

		%ifndef _CONST_MINUS_1
		% error "_CONST_MINUS_1 must be defined and must resolve to an address that holds the constant value 0x1FFFFFFF"
		%endif

		%define dw `dw'
		%define org `org'
		
		%macro jmp loc
			adc [_CONST_MINUS_1]
			jc loc
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local nop = opcode.make(29)

local entities = {}

local mnemonic_to_class_code = {
	[ "shl"] = { class = "1V", code = 0x00 },
	[ "shr"] = { class = "1V", code = 0x01 },
	[ "add"] = { class = "1M", code = 0x02 },
	[ "adc"] = { class = "1M", code = 0x12 },
	["flip"] = { class = "0V", code = 0x03 },
	[ "mov"] = { class = "1M", code = 0x04 },
	["sync"] = { class = "0V", code = 0x05 },
	[ "nop"] = { class = "0V", code = 0x06 },
	[  "jc"] = { class = "1V", code = 0x07 },
}

local mnemonic_desc = {}

function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local final_code = nop:clone()
	local instr_desc = mnemonic_to_class_code[mnemonic_token.value]
	final_code:merge(instr_desc.code, 24)

	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1]
			})

		elseif #ix_param == 3
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:number()
		   and ix_param[3]:punctuator("]") then
			table.insert(operands, {
				type = "immaddr",
				value = ix_param[2].parsed,
				token = ix_param[2]
			})
		   
		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local operand_mode_valid = false
	if instr_desc.class == "0V" then
		if #operands == 0 then
			operand_mode_valid = true
		end

	elseif instr_desc.class == "1V" then
		if #operands == 1 and operands[1].type == "imm" then
			operand_mode_valid = true
		end

	elseif instr_desc.class == "1M" then
		if #operands == 1 and operands[1].type == "immaddr" then
			operand_mode_valid = true
		end

	end
	if not operand_mode_valid then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end

	if #operands == 1 then
		local value = operands[1].value
		if value >= 0x1000000 then
			value = value % 0x1000000
			operands[1].token:blamef(printf.warn, "number truncated to 24 bits")
		end
		final_code:merge(value, 0)
	end

	return true, { final_code }
end

local dw_bits = 29

local mnemonics = {}
for key in pairs(mnemonic_to_class_code) do
	mnemonics[key] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end
	local space_available = 0x1000
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end
	for ix = 0, 0xFFF do
		sim.partProperty(sim.partID(x + (ix % 128) - 1, y + math.floor(ix / 128) - 34), "ctype", 0x20000000 + (opcodes[ix] and opcodes[ix].dwords[1] or 0))
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.r2", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local detect = require("tptasm.detect")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_
		
		%define dw `dw'
		%define org `org'

		%define ja jnbe
		%define jna jbe
		%define jae jnc
		%define jnae jc
		%define je jz
		%define jne jnz
		%define jnle jg
		%define jle jng
		%define jnl jge
		%define jl jnge
		%define jb jc
		%define jnb jnc
		%define test ands
		%define cmb sbbs
		%define cmp subs

		%macro nop
			jn r0
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local dw_bits = 29

local nop = opcode.make(32):merge(0x20000000, 0)

local entities = {
	[  "r0" ] = { type = "register", offset =  0 },
	[  "r1" ] = { type = "register", offset =  1 },
	[  "r2" ] = { type = "register", offset =  2 },
	[  "r3" ] = { type = "register", offset =  3 },
	[  "r4" ] = { type = "register", offset =  4 },
	[  "r5" ] = { type = "register", offset =  5 },
	[  "r6" ] = { type = "register", offset =  6 },
	[  "r7" ] = { type = "register", offset =  7 },
	[  "r8" ] = { type = "register", offset =  8 },
	[  "r9" ] = { type = "register", offset =  9 },
	[ "r10" ] = { type = "register", offset = 10 },
	[ "r11" ] = { type = "register", offset = 11 },
	[ "r12" ] = { type = "register", offset = 12 },
	[ "r13" ] = { type = "register", offset = 13 },
	[ "r14" ] = { type = "register", offset = 14 },
	[ "r15" ] = { type = "register", offset = 15 },
}
entities["bp"] = entities["r13"]
entities["sp"] = entities["r14"]
entities["ip"] = entities["r15"]

local mnemonics = {}

local mnemonic_to_class_code = {
    [ "mov"  ] = { class = "AB", code = 0x20000000 },
    [ "and"  ] = { class = "AB", code = 0x21000000 },
    [ "or"   ] = { class = "AB", code = 0x22000000 },
    [ "xor"  ] = { class = "AB", code = 0x23000000 },
    [ "add"  ] = { class = "AB", code = 0x24000000 },
    [ "adc"  ] = { class = "AB", code = 0x25000000 },
    [ "sub"  ] = { class = "AB", code = 0x26000000 },
    [ "sbb"  ] = { class = "AB", code = 0x27000000 },
    [ "swm"  ] = { class = " B", code = 0x28000000 },
    [ "ands" ] = { class = "AB", code = 0x29000000 },
    [ "ors"  ] = { class = "AB", code = 0x2A000000 },
    [ "xors" ] = { class = "AB", code = 0x2B000000 },
    [ "adds" ] = { class = "AB", code = 0x2C000000 },
    [ "adcs" ] = { class = "AB", code = 0x2D000000 },
    [ "subs" ] = { class = "AB", code = 0x2E000000 },
    [ "sbbs" ] = { class = "AB", code = 0x2F000000 },
    [ "hlt"  ] = { class = "  ", code = 0x30000000 },
    [ "jmp"  ] = { class = " B", code = 0x31000000 },
    [ "jn"   ] = { class = " B", code = 0x31000001 },
    [ "jc"   ] = { class = " B", code = 0x31000002 },
    [ "jnc"  ] = { class = " B", code = 0x31000003 },
    [ "jo"   ] = { class = " B", code = 0x31000004 },
    [ "jno"  ] = { class = " B", code = 0x31000005 },
    [ "js"   ] = { class = " B", code = 0x31000006 },
    [ "jns"  ] = { class = " B", code = 0x31000007 },
    [ "jz"   ] = { class = " B", code = 0x31000008 },
    [ "jnz"  ] = { class = " B", code = 0x31000009 },
    [ "jng"  ] = { class = " B", code = 0x3100000A },
    [ "jg"   ] = { class = " B", code = 0x3100000B },
    [ "jnge" ] = { class = " B", code = 0x3100000C },
    [ "jge"  ] = { class = " B", code = 0x3100000D },
    [ "jbe"  ] = { class = " B", code = 0x3100000E },
    [ "jnbe" ] = { class = " B", code = 0x3100000F },
    [ "rol"  ] = { class = "AB", code = 0x32000000 },
    [ "ror"  ] = { class = "AB", code = 0x33000000 },
    [ "shl"  ] = { class = "AB", code = 0x34000000 },
    [ "shr"  ] = { class = "AB", code = 0x35000000 },
    [ "scl"  ] = { class = "AB", code = 0x36000000 },
    [ "scr"  ] = { class = "AB", code = 0x37000000 },
    [ "bump" ] = { class = "A ", code = 0x38000000 },
    [ "wait" ] = { class = "A ", code = 0x39000000 },
    [ "send" ] = { class = "AB", code = 0x3A000000 },
    [ "recv" ] = { class = "AB", code = 0x3B000000 },
    [ "push" ] = { class = " B", code = 0x3C000000 },
    [ "pop"  ] = { class = "A ", code = 0x3D000000 },
    [ "call" ] = { class = " B", code = 0x3E000000 },
    [ "ret"  ] = { class = "  ", code = 0x3F000000 },
}
local class_to_mode = {
    ["  "] = {
        { ops = {                               }, code = 0x00000000 },
    },
    ["A "] = {
        { ops = {  "reg@0"                      }, code = 0x00000000 },
        { ops = { "[reg@0]"                     }, code = 0x00400000 },
        { ops = { "[reg@16+reg@0]"              }, code = 0x00C00000 },
        { ops = { "[reg@16-reg@0]"              }, code = 0x00C08000 },
        { ops = { "[imm16@4]"                   }, code = 0x00500000 },
        { ops = { "[imm11@4+reg@16]"            }, code = 0x00D00000 },
        { ops = { "[reg@16+imm11@4]"            }, code = 0x00D00000 },
        { ops = { "[reg@16-imm11@4]"            }, code = 0x00D08000 },
    },
    [" B"] = {
        { ops = {  "reg@4"                      }, code = 0x00000000 },
        { ops = { "[reg@4]"                     }, code = 0x00100000 },
        { ops = { "[reg@16+reg@4]"              }, code = 0x00900000 },
        { ops = { "[reg@16-reg@4]"              }, code = 0x00908000 },
        { ops = {  "imm16@4"                    }, code = 0x00200000 },
        { ops = { "[imm16@4]"                   }, code = 0x00300000 },
        { ops = { "[imm11@4+reg@16]"            }, code = 0x00B00000 },
        { ops = { "[reg@16+imm11@4]"            }, code = 0x00B00000 },
        { ops = { "[reg@16-imm11@4]"            }, code = 0x00B08000 },
    },
    ["AB"] = {
        { ops = {  "reg@0" ,  "reg@4"           }, code = 0x00000000 },
        { ops = {  "reg@0" , "[reg@4]"          }, code = 0x00100000 },
        { ops = {  "reg@0" , "[reg@16+reg@4]"   }, code = 0x00900000 },
        { ops = {  "reg@0" , "[reg@16-reg@4]"   }, code = 0x00908000 },
        { ops = {  "reg@0" ,  "imm16@4"         }, code = 0x00200000 },
        { ops = {  "reg@0" , "[imm16@4]"        }, code = 0x00300000 },
        { ops = {  "reg@0" , "[imm11@4+reg@16]" }, code = 0x00B00000 },
        { ops = {  "reg@0" , "[reg@16+imm11@4]" }, code = 0x00B00000 },
        { ops = {  "reg@0" , "[reg@16-imm11@4]" }, code = 0x00B08000 },
        { ops = { "[reg@0]",            "reg@4" }, code = 0x00400000 },
        { ops = { "[reg@16+reg@0]",     "reg@4" }, code = 0x00C00000 },
        { ops = { "[reg@16-reg@0]",     "reg@4" }, code = 0x00C08000 },
        { ops = { "[imm16@4]",          "reg@0" }, code = 0x00500000 },
        { ops = { "[imm11@4+reg@16]",   "reg@0" }, code = 0x00D00000 },
        { ops = { "[reg@16+imm11@4]",   "reg@0" }, code = 0x00D00000 },
        { ops = { "[reg@16-imm11@4]",   "reg@0" }, code = 0x00D08000 },
        { ops = { "[reg@0]",          "imm16@4" }, code = 0x00600000 },
        { ops = { "[reg@16+reg@0]",   "imm11@4" }, code = 0x00E00000 },
        { ops = { "[reg@16-reg@0]",   "imm11@4" }, code = 0x00E08000 },
        { ops = { "[imm16@4]",         "imm4@0" }, code = 0x00700000 },
        { ops = { "[imm11@4+reg@16]",  "imm4@0" }, code = 0x00F00000 },
        { ops = { "[reg@16+imm11@4]",  "imm4@0" }, code = 0x00F00000 },
        { ops = { "[reg@16-imm11@4]",  "imm4@0" }, code = 0x00F08000 },
    },
}

local mnemonic_desc = {}
function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 1
		   and ix_param[1]:is("entity") and ix_param[1].entity.type == "register" then
			table.insert(operands, {
				type = "reg",
				value = ix_param[1].entity.offset,
			})

		elseif #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1],
			})

		elseif #ix_param == 3
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:is("entity") and ix_param[2].entity.type == "register"
		   and ix_param[3]:punctuator("]") then
			table.insert(operands, {
				type = "[reg]",
				value = ix_param[2].entity.offset,
			})

		elseif #ix_param == 3
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:number()
		   and ix_param[3]:punctuator("]") then
			table.insert(operands, {
				type = "[imm]",
				value = ix_param[2].parsed,
				token = ix_param[2],
			})

		elseif #ix_param == 5
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:is("entity") and ix_param[2].entity.type == "register"
		   and ix_param[3]:punctuator("+")
		   and ix_param[4]:is("entity") and ix_param[4].entity.type == "register"
		   and ix_param[5]:punctuator("]") then
			table.insert(operands, {
				type = "[reg+reg]",
				base = ix_param[2].entity.offset,
				value = ix_param[4].entity.offset,
			})

		elseif #ix_param == 5
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:is("entity") and ix_param[2].entity.type == "register"
		   and ix_param[3]:punctuator("-")
		   and ix_param[4]:is("entity") and ix_param[4].entity.type == "register"
		   and ix_param[5]:punctuator("]") then
			table.insert(operands, {
				type = "[reg-reg]",
				base = ix_param[2].entity.offset,
				value = ix_param[4].entity.offset,
			})

		elseif #ix_param == 5
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:number()
		   and ix_param[3]:punctuator("+")
		   and ix_param[4]:is("entity") and ix_param[4].entity.type == "register"
		   and ix_param[5]:punctuator("]") then
			table.insert(operands, {
				type = "[reg+imm]",
				base = ix_param[4].entity.offset,
				value = ix_param[2].parsed,
				token = ix_param[2],
			})

		elseif #ix_param == 5
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:is("entity") and ix_param[2].entity.type == "register"
		   and ix_param[3]:punctuator("+")
		   and ix_param[4]:number()
		   and ix_param[5]:punctuator("]") then
			table.insert(operands, {
				type = "[reg+imm]",
				base = ix_param[2].entity.offset,
				value = ix_param[4].parsed,
				token = ix_param[4],
			})

		elseif #ix_param == 5
		   and ix_param[1]:punctuator("[")
		   and ix_param[2]:is("entity") and ix_param[2].entity.type == "register"
		   and ix_param[3]:punctuator("-")
		   and ix_param[4]:number()
		   and ix_param[5]:punctuator("]") then
			table.insert(operands, {
				type = "[reg-imm]",
				base = ix_param[2].entity.offset,
				value = ix_param[4].parsed,
				token = ix_param[4],
			})
		   
		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local final_code
	local class_code = mnemonic_to_class_code[mnemonic_token.value]
	if #class_code.class:gsub("[^AB]", "") == #operands then
		local modes = class_to_mode[class_code.class]
		for m = 1, #modes do
			local ops = modes[m].ops
			local ok = true
			for i = 1, #operands do
				if operands[i].type ~= ops[i]:gsub("[0-9@]", "") then
					ok = false
					break
				end
			end
			if ok then
				final_code = opcode.make(32):merge(class_code.code, 0):merge(modes[m].code, 0)
				for i = 1, #operands do
					local next_type = operands[i].type:gmatch("[a-z]+")
					local comp = 0
					for shift in ops[i]:gmatch("[0-9@]+") do
						comp = comp + 1
						local comp_type = next_type()
						local width = 4
						local value = operands[i].value
						if comp == 1 and operands[i].base then
							value = operands[i].base
						end
						if comp_type == "imm" then
							width, shift = shift:match("^([0-9]+)@([0-9]+)$")
							width = tonumber(width)
							shift = tonumber(shift)
						else
							shift = tonumber(shift:sub(2))
						end
						if value >= 2 ^ width then
							value = value % 2 ^ width
							operands[i].token:blamef(printf.warn, "number truncated to " .. width .. " bits")
						end
						final_code:merge(value, shift)
					end
				end
			end
		end
	end

	local hardware_bugs = false
	if mnemonic_token.value == "bump" and operands[1].type:find("^%[") then
		hardware_bugs = true
	end
	if mnemonic_token.value == "send" and operands[1].type:find("^%[") then
		hardware_bugs = true
	end
	if hardware_bugs then
		mnemonic_token:blamef(printf.err, "refusing to assemble due to hardware bugs, consult the manual")
		return false
	end

	if not final_code then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end

	return true, { final_code }
end

for mnemonic in pairs(mnemonic_to_class_code) do
	mnemonics[mnemonic] = mnemonic_desc
end

local supported_models = {
	[ "R216K2A" ] = { ram_x = 70, ram_y =  -99, ram_width = 128, ram_height = 16 },
	[ "R216K4A" ] = { ram_x = 70, ram_y = -115, ram_width = 128, ram_height = 32 },
	[ "R216K8B" ] = { ram_x = 70, ram_y = -147, ram_width = 128, ram_height = 64 },
}
local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local model_data = supported_models[model]
	local space_available = model_data.ram_width * model_data.ram_height
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end

	for row = 0, model_data.ram_height - 1 do
		local skipped = 0
		for column = 0, model_data.ram_width - 1 do
			while true do
				local id = sim.partID(x + model_data.ram_x - column - skipped, y + row + model_data.ram_y)
				if id and sim.partProperty(id, "type") ~= elem.DEFAULT_PT_FILT then
					id = nil
				end
				if id then
					local index = row * model_data.ram_width + column
					local opcode = opcodes[index] and opcodes[index].dwords[1] or nop.dwords[1]
					sim.partProperty(id, "ctype", opcode)
					break
				end
				if skipped > 0 then
					printf.err("RAM layout sanity check failed")
					return
				end
				skipped = 1
			end
		end
	end
end

return {
	includes = includes,
	dw_bits = dw_bits,
	nop = nop,
	entities = entities,
	mnemonics = mnemonics,
	flash = flash,
}

end)

require("register", "tptasm.archs.r3", function()
local printf = require("tptasm.printf")
local config = require("tptasm.config")
local opcode = require("tptasm.opcode")
local xbit32 = require("tptasm.xbit32")
local detect = require("tptasm.detect")

local includes = {
	["common"] = ([==[
		%ifndef _COMMON_INCLUDED_
		%define _COMMON_INCLUDED_
		
		%define dw `dw'
		%define org `org'

		%define ja jnbe
		%define jna jbe
		%define jae jnc
		%define jnae jc
		%define je jz
		%define jne jnz
		%define jg jnle
		%define jng jle
		%define jge jnl
		%define jnge jl
		%define jb jc
		%define jnb jnc

		%define jya jynbe
		%define jyna jybe
		%define jyae jync
		%define jynae jyc
		%define jye jyz
		%define jyne jynz
		%define jyg jynle
		%define jyng jyle
		%define jyge jynl
		%define jynge jyl
		%define jyb jyc
		%define jynb jync

		%macro test pri, sec
			and r0, pri, sec
		%endmacro

		%macro cmp pri, sec
			sub r0, pri, sec
		%endmacro

		%macro nop
			mov r0, r0, r1
		%endmacro

		%endif ; _COMMON_INCLUDED_
	]==]):gsub("`([^\']+)'", function(cap)
		return config.reserved[cap]
	end)
}

local dw_bits = 32

local nop = opcode.make(32):merge(0x00000000, 0)

local entities = {}
for i = 0, 31 do
	entities["r" .. i] = { type = "register", offset = i }
end

local mnemonics = {}

local function transform(tbl, func)
	local newtbl = {}
	for key, value in pairs(tbl) do
		func(newtbl, key, value)
	end
	return newtbl
end

local function shallow_copy(tbl)
	return transform(tbl, function(newtbl, key, value)
		newtbl[key] = value
	end)
end

local cond_info = {
	[    "" ] = { code = 0x00000000 },
	[  "be" ] = { code = 0x00100000 },
	[   "l" ] = { code = 0x00200000 },
	[  "le" ] = { code = 0x00300000 },
	[   "s" ] = { code = 0x00400000 },
	[   "z" ] = { code = 0x00500000 },
	[   "o" ] = { code = 0x00600000 },
	[   "c" ] = { code = 0x00700000 },
	[   "n" ] = { code = 0x00800000 },
	[ "nbe" ] = { code = 0x00900000 },
	[  "nl" ] = { code = 0x00A00000 },
	[ "nle" ] = { code = 0x00B00000 },
	[  "ns" ] = { code = 0x00C00000 },
	[  "nz" ] = { code = 0x00D00000 },
	[  "no" ] = { code = 0x00E00000 },
	[  "nc" ] = { code = 0x00F00000 },
}
local mnemonic_info = {
	-- F: updates flags, append s to the mnemonic to suppress them
	-- G: can update flags, append f to the mnemonic to enable this
	-- J: some kind of jump (secondary is encoded as primary)
	-- H: some kind of shift (immediate is limited to 4 bits)
	-- P: takes primary
	-- S: takes secondary
	-- T: takes tertiary
	-- D: secondary defaults to primary
	-- M: secondary defaults to tertiary, or r0 if tertiary is imm
	-- E: secondary defaults to r0
	-- X: syntactically swap secondary and tertiary
	[  "mov" ] = { traits = "GPSTM  ", code = 0x00000000 },
	[    "j" ] = { traits = "J STE  ", code = 0x01010000 },
	[   "jy" ] = { traits = "J STE  ", code = 0x00010000 },
	[   "ld" ] = { traits = " PSTE  ", code = 0x00020000 },
	[  "exh" ] = { traits = "FPSTD  ", code = 0x00030000 },
	[  "sub" ] = { traits = "FPSTDX ", code = 0x00040000, companion_code_xor = 0x00020000, companion_add_one = true  },
	[  "sbb" ] = { traits = "FPSTDX ", code = 0x00050000, companion_code_xor = 0x00020000, companion_add_one = false },
	[  "add" ] = { traits = "FPSTD  ", code = 0x00060000 },
	[  "adc" ] = { traits = "FPSTD  ", code = 0x00070000 },
	[   "or" ] = { traits = "FPSTD  ", code = 0x00090000 },
	[  "xor" ] = { traits = "FPSTD  ", code = 0x00080000 },
	[   "st" ] = { traits = " PSTE  ", code = 0x000A0000 },
	[  "shl" ] = { traits = "FPSTD H", code = 0x000B0000 },
	[  "shr" ] = { traits = "FPSTD H", code = 0x000B8000 },
	[  "and" ] = { traits = "FPSTD  ", code = 0x000C0000 },
	[  "hlt" ] = { traits = "       ", code = 0x000D0000 },
	[  "mul" ] = { traits = " PST   ", code = 0x000E0000 },
	[ "mulh" ] = { traits = " PST   ", code = 0x000F0000 },
	[ "muls" ] = { traits = " PST   ", code = 0x800E0000 },
	[ "mulx" ] = { traits = " PST   ", code = 0x800F0000 },
}
mnemonic_info = transform(mnemonic_info, function(newtbl, key, value)
	if value.traits:find("F") then
		newtbl[key .. "s"] = value
		local nvalue = shallow_copy(value)
		nvalue.code = xbit32.bor(nvalue.code, 0x80000000)
		newtbl[key] = nvalue
	elseif value.traits:find("G") then
		newtbl[key] = value
		local nvalue = shallow_copy(value)
		nvalue.code = xbit32.bor(nvalue.code, 0x80000000)
		newtbl[key .. "f"] = nvalue
	else
		newtbl[key] = value
	end
end)
mnemonic_info = transform(mnemonic_info, function(newtbl, key, value)
	if value.traits:find("J") then
		for ckey, cvalue in pairs(cond_info) do
			local nvalue = shallow_copy(value)
			nvalue.code = xbit32.bor(nvalue.code, cvalue.code)
			local nkey = key .. ckey
			if nkey == "j" then
				nkey = "jmp"
			end
			newtbl[nkey] = nvalue
		end
	else
		newtbl[key] = value
	end
end)

local mnemonic_desc = {}
function mnemonic_desc.length()
	return true, 1 -- * RISC :)
end

function mnemonic_desc.emit(mnemonic_token, parameters)
	local operands = {}
	for ix, ix_param in ipairs(parameters) do
		if #ix_param == 1
		   and ix_param[1]:is("entity") and ix_param[1].entity.type == "register" then
			table.insert(operands, {
				type = "reg",
				value = ix_param[1].entity.offset
			})

		elseif #ix_param == 1
		   and ix_param[1]:number() then
			table.insert(operands, {
				type = "imm",
				value = ix_param[1].parsed,
				token = ix_param[1]
			})
		   
		else
			if ix_param[1] then
				ix_param[1]:blamef(printf.err, "operand format not recognised")
			else
				ix_param.before:blamef_after(printf.err, "operand format not recognised")
			end
			return false

		end
	end

	local info = mnemonic_info[mnemonic_token.value]
	local warnings_emitted = {}
	local named_ops = {}

	local code = info.code
	local named_op_index = 1
	local imm_index = 3
	if info.traits:find("X") then
		imm_index = 2
	end
	local imm_count = 0
	for ix_operand = 1, #operands do
		local operand = operands[ix_operand]
		if operand and operand.type == "imm" then
			imm_count = imm_count + 1
			local trunc_bits = 16
			if info.traits:find("H") then
				trunc_bits = 4
			end
			local trunc = 2 ^ trunc_bits
			if operand.value < 0 and operand.value >= -trunc / 2 then
				operand.value = operand.value + trunc
			end
			if operand.value >= trunc then
				operand.value = operand.value % trunc
				operand.token:blamef(printf.warn, "number truncated to %i bits", trunc_bits)
			end
			if named_op_index < imm_index then
				named_op_index = imm_index
			end
		end
		if named_op_index == 1 and not info.traits:find("P") then
			named_op_index = 2
		end
		if named_op_index == 2 and not info.traits:find("S") then
			named_op_index = 3
		end
		if named_op_index == 3 and not info.traits:find("T") then
			named_op_index = 4
		end
		named_ops[named_op_index] = operand
		named_op_index = named_op_index + 1
	end
	if info.traits:find("T") and not named_ops[3] and named_ops[2] then
		named_ops[3], named_ops[2] = named_ops[2], named_ops[3]
	end
	if info.traits:find("X") then
		if info.traits:find("T") and named_ops[3] and named_ops[3].type == "imm" then
			named_ops[3].value = xbit32.bxor(named_ops[3].value, 0xFFFF)
			if info.companion_add_one then
				named_ops[3].value = xbit32.band(named_ops[3].value + 1, 0xFFFF)
			end
			code = xbit32.bxor(code, info.companion_code_xor)
		else
			named_ops[3], named_ops[2] = named_ops[2], named_ops[3]
		end
	end

	local final_code = nop:clone():merge(code, 0)
	if imm_count > 1 then
		final_code = nil
	end
	if final_code and info.traits:find("P") then
		if named_ops[1] then
			final_code = final_code:merge(named_ops[1].value, 25)
		else
			final_code = nil
		end
	end
	if final_code and info.traits:find("S") then
		local shift = info.traits:find("J") and 25 or 20
		if named_ops[2] then
			final_code = final_code:merge(named_ops[2].value, shift)
		elseif info.traits:find("D") and named_ops[1] then
			final_code = final_code:merge(named_ops[1].value, shift)
		elseif info.traits:find("M") and named_ops[3] then
			if named_ops[3].type == "imm" then
				final_code = final_code:merge(entities["r0"].offset, shift)
			else
				final_code = final_code:merge(named_ops[3].value, shift)
			end
		elseif info.traits:find("E") then
			final_code = final_code:merge(entities["r0"].offset, shift)
		else
			final_code = nil
		end
	end
	if final_code and info.traits:find("T") then
		if named_ops[3] then
			if named_ops[3].type == "imm" then
				final_code = final_code:merge(named_ops[3].value, 0):merge(0x40000000, 0)
			else
				final_code = final_code:merge(named_ops[3].value, 0)
			end
		elseif info.traits:find("X") and info.traits:find("D") and named_ops[1] then
			final_code = final_code:merge(named_ops[1].value, 0)
		else
			final_code = nil
		end
	end
	if named_ops[4] then
		final_code = nil
	end

	if not final_code then
		local operands_repr = {}
		for _, ix_oper in ipairs(operands) do
			table.insert(operands_repr, ix_oper.type)
		end
		mnemonic_token:blamef(printf.err, "no variant of %s exists that takes '%s' operands", mnemonic_token.value, table.concat(operands_repr, ", "))
		return false
	end
	return true, { final_code }
end
for mnemonic in pairs(mnemonic_info) do
	mnemonics[mnemonic] = mnemonic_desc
end

local function flash(model, target, opcodes)
	local x, y = detect.cpu(model, target)
	if not x then
		return
	end

	local memory_rows_str, core_count_str = assert(model:match("^R3A(..)(..)$"))
	local memory_rows = tonumber(memory_rows_str)
	local core_count = tonumber(core_count_str)
	local space_available = memory_rows * 128
	if #opcodes >= space_available then
		printf.err("out of space; code takes %i cells, only have %i", #opcodes + 1, space_available)
		return
	end

	local row_size = 128
	local row_count = space_available / row_size
	for index = 0, space_available - 1 do
		local index_x = index % row_size
		local index_y = math.floor(index / row_size)
		local opcode = opcodes[index] and opcodes[index].dwords[1] or xbit32.bor(xbit32.band(xbit32.bxor(index_y, index_x), 0xFFFF), 0x00308000)
		if xbit32.band(opcode, 0x3FFFFFFF) == 0 then
			opcode = xbit32.bxor(opcode, 0x20000000)
		end
		sim.partProperty(sim.partID(x + index_x - 41, y + index_y - 13 - row_count - core_count * 6), "ctype", opcode)
	end
end

return {
	includes  = includes,
	dw_bits   = dw_bits,
	nop       = nop,
	entities  = entities,
	mnemonics = mnemonics,
	flash     = flash,
}

end)

require("register", "tptasm.config", function()
local config = {
	max_depth = {
		include = 100,
		expansion = 100,
		eval = 100
	},
	reserved = {
		appendvararg = "_Appendvararg",
		defined      = "_Defined",
		dw           = "_Dw",
		identity     = "_Identity",
		labelcontext = "_Labelcontext",
		litmap       = "_Litmap",
		macrounique  = "_Macrounique",
		model        = "_Model",
		next         = "_Next",
		org          = "_Org",
		peerlabel    = "_Peerlabel",
		superlabel   = "_Superlabel",
		this         = "_This",
		vararg       = "_Vararg",
		varargsize   = "_Varargsize",
	},
	litmap = {}
}
for ix = 0, 127 do
	config.litmap[ix] = ix
end

return config

end)

require("register", "tptasm.detect", function()
local printf = require("tptasm.printf")
local xbit32 = require("tptasm.xbit32")

local function enumerate_standard(id)
	if  sim.partProperty(id, "ctype") == 0x1864A205
	and sim.partProperty(id, "type") == elem.DEFAULT_PT_QRTZ then
		local x, y = sim.partPosition(id)
		local dxdyprop = sim.partProperty(id, "tmp2")
		local dx   = xbit32.band(              dxdyprop,      0xF)
		local dy   = xbit32.band(xbit32.rshift(dxdyprop, 4),  0xF)
		local prop = xbit32.band(xbit32.rshift(dxdyprop, 8), 0x1F)
		if dx > 8 then
			dx = dx - 16
		end
		if dy > 8 then
			dy = dy - 16
		end
		if dx == 0 and dy == 0 then
			dx = 1
		end
		if math.abs(dx) > 1 or math.abs(dy) > 1 then -- * Garbage.
			dy = 0
			dx = 1
		end
		if prop == 0 then
			prop = sim.FIELD_CTYPE
		end
		local function prop_of(offs)
			local cid = sim.partID(x + offs * dx, y + offs * dy)
			return cid and sim.partProperty(cid, prop)
		end
		local id_target = prop_of(-1)
		if id_target then
			local offs = 0
			local id_model = ""
			local checksum = 0
			local name_intact = true
			while true do
				offs = offs + 1
				local ctype = prop_of(offs)
				if not ctype or ctype < 0 or ctype > 255 then
					name_intact = false
					break
				end
				if ctype == 0 then
					break
				end
				id_model = id_model .. string.char(ctype)
				checksum = checksum + ctype
			end
			if name_intact and prop_of(offs + 1) == checksum then
				coroutine.yield(x, y, id_model, id_target)
				return true
			end
		end
	end
	return false
end

local function enumerate_nope()
	-- * nothing, it's a placeholder and it just fails
end

local function match_property(id, name, value)
	return not value or sim.partProperty(id, name) == value
end

local function enumerate_legacy(model, conditions)
	return function(id)
		if  match_property(id, "type", conditions[1][3])
		and match_property(id, "ctype", conditions[1][4]) then
			local x, y = sim.partPosition(id)
			local ok = true
			for ix = 2, #conditions do
				local cid = sim.partID(x + conditions[ix][1], y + conditions[ix][2])
				if not cid
				or not match_property(cid, "type", conditions[ix][3])
				or not match_property(cid, "ctype", conditions[ix][4]) then
					ok = false
				end
			end
			if ok then
				coroutine.yield(x, y, model, 0)
				return true
			end
		end
		return false
	end
end

local enumerate_micro21 = enumerate_nope
local enumerate_maps = enumerate_nope
if _G.tpt then
	enumerate_micro21 = enumerate_legacy("MICRO21", {
		{  nil, nil, elem.DEFAULT_PT_DTEC, elem.DEFAULT_PT_DSTW },
		{ -181, 292, elem.DEFAULT_PT_DMND,                false },
		{  336, 201, elem.DEFAULT_PT_DMND,                false },
		{  394,  23, elem.DEFAULT_PT_BTRY,                false },
		{  384,  85, elem.DEFAULT_PT_BTRY,                false },
		{  -13, 177, elem.DEFAULT_PT_BTRY,                false },
		{ -179, 306, elem.DEFAULT_PT_PTCT,                false },
		{ -175, 306, elem.DEFAULT_PT_PTCT,                false },
		{ -178, 309, elem.DEFAULT_PT_PTCT,                false },
		{ -174, 309, elem.DEFAULT_PT_PTCT,                false },
	})
	enumerate_maps = enumerate_legacy("MAPS", {
		{  nil, nil, elem.DEFAULT_PT_SWCH, false },
		{  -28,  -2, elem.DEFAULT_PT_ARAY, false },
		{  137, -27, elem.DEFAULT_PT_DLAY, false },
		{   67, -11, elem.DEFAULT_PT_INST, false },
		{   49,  19, elem.DEFAULT_PT_FILT, false },
		{  -12,  34, elem.DEFAULT_PT_INWR, false },
		{   90,   3, elem.DEFAULT_PT_ARAY, false },
		{   94, -42, elem.DEFAULT_PT_INSL, false },
		{  124,  12, elem.DEFAULT_PT_SWCH, false },
		{  113,  11, elem.DEFAULT_PT_METL, false },
	})
end

local function enumerate_cpus()
	if not _G.tpt then
		printf.err("not running inside TPT, can't find target")
		return
	end
	for id in sim.parts() do
		local _ = enumerate_standard(id)
		       or enumerate_micro21(id)
		       or enumerate_maps(id)
	end
end

local function all_cpus()
	local co = coroutine.create(function()
		local rethrow = false
		xpcall_wrap(function()
			enumerate_cpus()
		end, function(err)
			rethrow = true
		end)()
		if rethrow then
			error("rethrowing error from inside xpcall")
		end
	end)
	return function()
		if coroutine.status(co) ~= "dead" then
			local ok, x, y, id_model, id_target = coroutine.resume(co)
			if not ok then
				error(x)
			end
			return x, y, id_model, id_target
		end
	end
end

local function detect_filter(filter)
	local candidates = {}
	for x, y, model, id in all_cpus() do
		if filter(x, y, model, id) then
			table.insert(candidates, {
				x = x,
				y = y,
				model = model,
				id = id
			})
		end
	end
	if _G.tpt then
		local mx, my = sim.adjustCoords(tpt.mousex, tpt.mousey)
		for _, candidate in ipairs(candidates) do
			if candidate.x == mx and candidate.y == my then
				candidates = { candidate }
				break
			end
		end
	end
	if candidates[1] then
		return candidates[1]
	end
end

local function cpu(model_in, target_in)
	local candidate = detect_filter(function(x, y, model, id)
		return (not target_in or target_in == id   )
		   and (not  model_in or  model_in == model)
	end)
	if candidate then
		return candidate.x, candidate.y
	end
end

local function model(target_in)
	local candidate = detect_filter(function(x, y, model, id)
		return (not target_in or target_in == id)
	end)
	if candidate then
		return candidate.model
	end
end

local function make_anchor(model, dxstr, dystr, propname, leetid)
	if not _G.tpt then
		printf.err("not running inside TPT, can't spawn anchor")
		return
	end
	local prop = sim["FIELD_" .. tostring(propname or "ctype"):upper()]
	if not prop then
		printf.err("invalid property")
		return
	end
	local dx = tonumber(dxstr or "1")
	if dx ~= math.floor(dx) or dx >= 8 or dx < -8 then
		printf.err("invalid dx")
		return
	end
	local edx = dx
	if edx < 0 then
		edx = edx + 16
	end
	local dy = tonumber(dystr or "0")
	if dy ~= math.floor(dy) or dy >= 8 or dy < -8 then
		printf.err("invalid dy")
		return
	end
	local edy = dy
	if edy < 0 then
		edy = edy + 16
	end
	local x, y = sim.adjustCoords(tpt.mousex, tpt.mousey)
	local function spawn(offs, ty)
		local px = x + offs * dx
		local py = y + offs * dy
		local id = sim.partID(px, py) or sim.partCreate(-2, px, py, ty)
		return id
	end
	sim.partProperty(spawn(-1, elem.DEFAULT_PT_FILT), prop, leetid or 1337)
	local anchor = spawn(0, elem.DEFAULT_PT_QRTZ)
	sim.partProperty(anchor, "tmp2", edx + edy * 0x10 + prop * 0x100)
	sim.partProperty(anchor, "ctype", 0x1864A205)
	local checksum = 0
	for ix = 1, #model do
		local byte = model:byte(ix)
		sim.partProperty(spawn(ix, elem.DEFAULT_PT_FILT), prop, byte)
		checksum = checksum + byte
	end
	spawn(#model + 1, elem.DEFAULT_PT_FILT)
	sim.partProperty(spawn(#model + 2, elem.DEFAULT_PT_FILT), prop, checksum)
end

return {
	all_cpus = all_cpus,
	cpu = cpu,
	model = model,
	make_anchor = make_anchor,
}

end)

require("register", "tptasm.emit", function()
local printf  = require("tptasm.printf")
local resolve = require("tptasm.resolve")

return function(architecture, to_emit, labels)
	local opcodes = {}
	do
		local max_pointer = 0
		for _, rec in pairs(to_emit) do
			local after_end = rec.offset + rec.length
			if max_pointer < after_end then
				max_pointer = after_end
			end
		end
		for ix = 0, max_pointer - 1 do
			opcodes[ix] = architecture.nop:clone()
		end
	end
	for offset, rec in pairs(to_emit) do
		if type(rec.emit) == "function" then
			local emission_ok = true
			for ix, ix_param in ipairs(rec.parameters) do
				local labels_ok, ix, err = resolve.label_offsets(false, ix_param, labels, rec)
				if labels_ok then
					local evals_ok, ix, jx, err = resolve.evaluations(false, ix_param, labels, rec)
					if evals_ok then
						local numbers_ok, ix, err = resolve.numbers(false, ix_param)
						if not numbers_ok then
							ix_param[ix]:blamef(printf.err, "invalid number: %s", err)
							emission_ok = false
						end
					else
						ix_param[ix].value[jx]:blamef(printf.err, "evaluation failed: %s", err)
						emission_ok = false
					end
				else
					ix_param[ix]:blamef(printf.err, "failed to resolve label: %s", err)
					emission_ok = false
				end
			end
			if emission_ok then
				local emitted
				emission_ok, emitted = rec.emit(rec.emitted_by, rec.parameters, offset)
				if emission_ok then
					for ix = 1, rec.length do
						opcodes[offset + ix - 1] = emitted[ix]
					end
				end
			end
			if not emission_ok then
				printf.err_called = true
			end

		elseif type(rec.emit) == "table" then
			for ix = 1, rec.length do
				opcodes[offset + ix - 1] = rec.emit[ix]
			end

		end
	end
	if printf.err_called then
		printf.failf("opcode emission stage failed, bailing")
	end

	return opcodes
end

end)

require("register", "tptasm.evaluate", function()
local config = require("tptasm.config")
local xbit32 = require("tptasm.xbit32")

local operator_funcs = {
	[">="] = { params = { "number", "number" }, does = function(a, b) return (a >= b) and 1 or 0 end },
	["<="] = { params = { "number", "number" }, does = function(a, b) return (a <= b) and 1 or 0 end },
	[">" ] = { params = { "number", "number" }, does = function(a, b) return (a >  b) and 1 or 0 end },
	["<" ] = { params = { "number", "number" }, does = function(a, b) return (a <  b) and 1 or 0 end },
	["=="] = { params = { "number", "number" }, does = function(a, b) return (a == b) and 1 or 0 end },
	["!="] = { params = { "number", "number" }, does = function(a, b) return (a ~= b) and 1 or 0 end },
	["&&"] = { params = { "number", "number" }, does = function(a, b) return (a ~= 0 and b ~= 0) and 1 or 0 end },
	["||"] = { params = { "number", "number" }, does = function(a, b) return (a ~= 0 or  b ~= 0) and 1 or 0 end },
	["!" ] = { params = { "number"           }, does = function(a) return (a == 0) and 1 or 0 end },
	["~" ] = { params = { "number"           }, does = function(a) return xbit32.bxor(a, 0xFFFFFFFF) end },
	["<<"] = { params = { "number", "number" }, does = xbit32.lshift },
	[">>"] = { params = { "number", "number" }, does = xbit32.rshift },
	["-" ] = { params = { "number", "number" }, does =    xbit32.sub },
	["+" ] = { params = { "number", "number" }, does =    xbit32.add },
	["/" ] = { params = { "number", "number" }, does =    xbit32.div },
	["%" ] = { params = { "number", "number" }, does =    xbit32.mod },
	["*" ] = { params = { "number", "number" }, does =    xbit32.mul },
	["&" ] = { params = { "number", "number" }, does =   xbit32.band },
	["|" ] = { params = { "number", "number" }, does =    xbit32.bor },
	["^" ] = { params = { "number", "number" }, does =   xbit32.bxor },
	[config.reserved.defined] = { params = { "alias" }, does = function(a) return a and 1 or 0 end },
	[config.reserved.identity] = { params = { "number" }, does = function(a) return a end },
}
local operators = {}
for key in pairs(operator_funcs) do
	table.insert(operators, key)
end
table.sort(operators, function(a, b)
	return #a > #b
end)

local function evaluate_composite(composite)
	if composite.type == "number" then
		return composite.value
	end
	return composite.operator.does(function(ix)
		return evaluate_composite(composite.operands[ix])
	end)
end

return function(tokens, cursor, last, aliases)
	local stack = {}

	local function apply_operator(operator_name)
		local operator = operator_funcs[operator_name]
		if #stack < #operator.params then
			return false, cursor, ("operator takes %i operands, %i supplied"):format(#operator.params, #stack)
		end
		local max_depth = 0
		local operands = {}
		for ix = #stack - #operator.params + 1, #stack do
			if max_depth < stack[ix].depth then
				max_depth = stack[ix].depth
			end
			table.insert(operands, stack[ix])
			stack[ix] = nil
		end
		if max_depth > config.max_depth.eval then
			return false, cursor, "maximum evaluation depth reached"
		end
		for ix = 1, #operands do
			if operator.params[ix] == "number" then
				if operands[ix].type == "number" then
					operands[ix] = operands[ix].value
				elseif operands[ix].type == "alias" then
					local alias = operands[ix].value
					local ok, number
					if #alias == 1 then
						ok, number = alias[1]:parse_number()
					end
					if not ok then
						return false, operands[ix].position, ("operand %i does not expand to a number"):format(ix)
					end
					operands[ix] = number
				else
					return false, operands[ix].position, ("operand %i is %s, should be number"):format(ix, operands[ix].type)
				end
			elseif operator.params[ix] == "alias" then
				if operands[ix].type == "alias" then
					operands[ix] = operands[ix].value
				else
					return false, operands[ix].position, ("operand %i is %s, should be alias"):format(ix, operands[ix].type)
				end
			end
		end
		table.insert(stack, {
			type = "number",
			value = operator.does(unpack(operands)),
			position = cursor,
			depth = max_depth + 1
		})
		return true
	end

	while cursor <= last do
		if tokens[cursor]:number() then
			local ok, number = tokens[cursor]:parse_number()
			if not ok then
				return false, cursor, ("invalid number: %s"):format(number)
			end
			table.insert(stack, {
				type = "number",
				value = number,
				position = cursor,
				depth = 1
			})
			cursor = cursor + 1

		elseif tokens[cursor]:punctuator() then
			local found
			for _, known_operator in ipairs(operators) do
				local matches = true
				for pos, ch in known_operator:gmatch("()(.)") do
					local relative = cursor + pos - 1
					if (relative > last)
					or (pos < #known_operator and tokens[relative].whitespace_follows)
					or (not tokens[relative]:punctuator(ch)) then
						matches = false
						break
					end
				end
				if matches then
					found = known_operator
					break
				end
			end
			if not found then
				return false, cursor, "unknown operator"
			end
			local ok, pos, err = apply_operator(found)
			if not ok then
				return false, pos, err
			end
			cursor = cursor + #found

		elseif tokens[cursor]:identifier() and operator_funcs[tokens[cursor].value] then
			local ok, pos, err = apply_operator(tokens[cursor].value)
			if not ok then
				return false, pos, err
			end

		elseif tokens[cursor]:identifier() and aliases[tokens[cursor].value] then
			table.insert(stack, {
				type = "alias",
				value = aliases[tokens[cursor].value],
				position = cursor,
				depth = 1
			})
			cursor = cursor + 1

		else
			return false, cursor, "not a number, an identifier or an operator"

		end
	end

	local ok, pos, err = apply_operator(config.reserved.identity)
	if not ok then
		return false, pos, err
	end
	if #stack > 1 then
		return false, stack[#stack - 1].position, "excess value"
	end
	if #stack < 1 then
		return false, 1, "no value"
	end
	return true, stack[1].value
end

end)

require("register", "tptasm.opcode", function()
local xbit32 = require("tptasm.xbit32")

local opcode_i = {}
local opcode_mt = { __index = opcode_i }

function opcode_i:clone()
	local dwords = {}
	for ix, ix_dword in ipairs(self.dwords) do
		dwords[ix] = ix_dword
	end
	return setmetatable({
		dwords = dwords
	}, opcode_mt)
end

function opcode_i:dump()
	return ("%08X "):rep(#self.dwords):format(unpack(self.dwords))
end

function opcode_i:has(shift)
	return math.floor(self.dwords[math.floor(shift / 32) + 1] / 2 ^ (shift % 32)) % 2 == 1
end

function opcode_i:merge(thing, shift)
	if type(thing) == "table" then
		for ix, ix_dword in ipairs(thing.dwords) do
			self:merge(ix_dword, (ix - 1) * 32 + shift)
		end
	else
		local offs = 1
		while shift >= 32 do
			offs = offs + 1
			shift = shift - 32
		end
		self.dwords[offs] = xbit32.bor(self.dwords[offs], thing % 2 ^ (32 - shift) * 2 ^ shift)
		thing = math.floor(thing / 2 ^ (32 - shift))
		for ix = offs + 1, #self.dwords do
			if thing == 0 then
				break
			end
			self.dwords[ix] = xbit32.bor(self.dwords[ix], thing % 0x100000000)
			thing = math.floor(thing / 0x100000000)
		end
	end
	return self
end

local function make(size)
	local dwords = {}
	for ix = 1, math.ceil(size / 32) do
		dwords[ix] = 0
	end
	return setmetatable({
		dwords = dwords
	}, opcode_mt)
end

return {
	make = make,
}

end)

require("register", "tptasm.preprocess", function()
local printf   = require("tptasm.printf")
local config   = require("tptasm.config")
local evaluate = require("tptasm.evaluate")
local resolve  = require("tptasm.resolve")
local tokenise = require("tptasm.tokenise")
local utility  = require("tptasm.utility")

local source_line_i = {}
local source_line_mt = { __index = source_line_i }

function source_line_i:dump_itop()
	local included_from = self.itop
	while included_from do
		printf.info("  included from %s:%i", included_from.path, included_from.line)
		included_from = included_from.next
	end
end

function source_line_i:blamef(report, format, ...)
	report("%s:%i: " .. format, self.path, self.line, ...)
	self:dump_itop()
end

function source_line_i:blamef_after(report, token, format, ...)
	report("%s:%i:%i " .. format, self.path, self.line, token.soffs + #token.value, ...)
	self:dump_itop()
end

return function(architecture, path)
	local macro_invocation_counter = 0
	local lines = {}
	local include_top = false
	local include_depth = 0

	local function preprocess_fail()
		printf.failf("preprocessing stage failed, bailing")
	end

	local aliases = {}
	local function expand_aliases(tokens, first, last, depth)
		local expanded = {}
		for ix = first, last do
			local alias = tokens[ix]:identifier() and aliases[tokens[ix].value]
			if alias then
				if depth > config.max_depth.expansion then
					tokens[ix]:blamef(printf.err, "maximum expansion depth reached while expanding alias '%s'", tokens[ix].value)
					preprocess_fail()
				end
				for _, token in ipairs(expand_aliases(alias, 1, #alias, depth + 1)) do
					table.insert(expanded, token:expand_by(tokens[ix]))
				end
			else
				table.insert(expanded, tokens[ix])
			end
		end
		return expanded
	end
	local function define(identifier, tokens, first, last)
		if aliases[identifier.value] then
			identifier:blamef(printf.err, "alias '%s' is defined", identifier.value)
			preprocess_fail()
		end
		local alias = {}
		for ix = first, last do
			table.insert(alias, tokens[ix])
		end
		aliases[identifier.value] = alias
	end
	local function undef(identifier)
		if not aliases[identifier.value] then
			identifier:blamef(printf.err, "alias '%s' is not defined", identifier.value)
			preprocess_fail()
		end
		aliases[identifier.value] = nil
	end

	local macros = {}
	local defining_macro = false
	local function expand_macro(tokens, depth)
		local expanded = expand_aliases(tokens, 1, #tokens, depth + 1)
		local macro = expanded[1]:identifier() and macros[expanded[1].value]
		if macro then
			if depth > config.max_depth.expansion then
				expanded[1]:blamef(printf.err, "maximum expansion depth reached while expanding macro '%s'", expanded[1].value)
				preprocess_fail()
			end
			local expanded_lines = {}
			local parameters_passed = {}
			local parameter_list = resolve.parameters(expanded[1], expanded, 2, #expanded)
			for ix, ix_param in ipairs(parameter_list) do
				parameters_passed[macro.params[ix] or false] = ix_param
			end
			if macro.vararg then
				if #macro.params > #parameter_list then
					expanded[1]:blamef(printf.err, "macro '%s' invoked with %i parameters, expects at least %i", expanded[1].value, #parameter_list, #macro.params)
					preprocess_fail()
				end
			else
				if #macro.params ~= #parameter_list then
					expanded[1]:blamef(printf.err, "macro '%s' invoked with %i parameters, expects %i", expanded[1].value, #parameter_list, #macro.params)
					preprocess_fail()
				end
			end
			macro_invocation_counter = macro_invocation_counter + 1
			parameters_passed[config.reserved.macrounique] = { expanded[1]:point({
				type = "identifier",
				value = ("_%i_"):format(macro_invocation_counter)
			}) }
			if macro.vararg then
				local vararg_param = {}
				local appendvararg_param = {}
				if #parameter_list > #macro.params then
					table.insert(appendvararg_param, expanded[1]:point({
						type = "punctuator",
						value = ","
					}))
				end
				for ix = #macro.params + 1, #parameter_list do
					for _, ix_token in ipairs(parameter_list[ix]) do
						table.insert(vararg_param, ix_token)
						table.insert(appendvararg_param, ix_token)
					end
					if ix ~= #parameter_list then
						table.insert(vararg_param, parameter_list[ix + 1].before)
						table.insert(appendvararg_param, parameter_list[ix + 1].before)
					end
				end
				parameters_passed[config.reserved.vararg] = vararg_param
				parameters_passed[config.reserved.appendvararg] = appendvararg_param
				parameters_passed[config.reserved.varargsize] = { expanded[1]:point({
					type = "number",
					value = tostring(#parameter_list - #macro.params)
				}) }
			end
			local old_aliases = {}
			for param, value in pairs(parameters_passed) do
				old_aliases[param] = aliases[param]
				aliases[param] = value
			end
			for _, line in ipairs(macro) do
				for _, expanded_line in ipairs(expand_macro(line.tokens, depth + 1)) do
					local cloned_line = {}
					for _, token in ipairs(expanded_line) do
						table.insert(cloned_line, token:expand_by(expanded[1]))
					end
					table.insert(expanded_lines, cloned_line)
				end
			end
			for param, value in pairs(parameters_passed) do
				aliases[param] = old_aliases[param]
			end
			return expanded_lines
		else
			return { expanded }
		end
	end
	local function macro(identifier, tokens, first, last)
		if macros[identifier.value] then
			identifier:blamef(printf.err, "macro '%s' is defined", identifier.value)
			preprocess_fail()
		end
		local params = {}
		local params_assoc = {}
		local vararg = false
		for ix = first, last, 2 do
			if  ix + 2 == last
			and tokens[ix    ]:punctuator(".") and not tokens[ix    ].whitespace_follows
			and tokens[ix + 1]:punctuator(".") and not tokens[ix + 1].whitespace_follows
			and tokens[ix + 2]:punctuator(".") then
				vararg = true
				break
			end
			if not tokens[ix]:identifier() then
				tokens[ix]:blamef(printf.err, "expected parameter name")
				preprocess_fail()
			end
			if params_assoc[tokens[ix].value] then
				tokens[ix]:blamef(printf.err, "duplicate parameter")
				preprocess_fail()
			end
			params_assoc[tokens[ix].value] = true
			table.insert(params, tokens[ix].value)
			if ix == last then
				break
			end
			if not tokens[ix + 1]:punctuator(",") then
				tokens[ix + 1]:blamef(printf.err, "expected comma")
				preprocess_fail()
			end
		end
		defining_macro = {
			params = params,
			name = identifier.value,
			vararg = vararg
		}
	end
	local function endmacro()
		macros[defining_macro.name] = defining_macro
		defining_macro = false
	end
	local function unmacro(identifier)
		if not macros[identifier.value] then
			identifier:blamef(printf.err, "macro '%s' is not defined", identifier.value)
			preprocess_fail()
		end
		macros[identifier.value] = nil
	end

	local condition_stack = { {
		condition = true,
		seen_else = false,
		been_true = true,
		opened_by = false
	} }

	local function include(base_path, relative_path, lines, req)
		if include_depth > config.max_depth.include then
			req:blamef(printf.err, "maximum include depth reached while including '%s'", relative_path)
			preprocess_fail()
		end
		local path = relative_path
		local content = architecture.includes[relative_path]
		if not content then
			path = base_path and utility.resolve_relative(base_path, relative_path) or relative_path
			local handle = io.open(path, "r")
			if not handle then
				req:blamef(printf.err, "failed to open '%s' for reading", path)
				preprocess_fail()
			end
			content = handle:read("*a")
			handle:close()
		end

		local line_number = 0
		for line in (content .. "\n"):gmatch("([^\n]*)\n") do
			line_number = line_number + 1
			local sline = setmetatable({
				path = path,
				line = line_number,
				itop = include_top,
				str = line
			}, source_line_mt)
			local ok, tokens, err = tokenise(sline)
			if not ok then
				printf.err("%s:%i:%i: %s", sline.path, sline.line, tokens, err)
				preprocess_fail()
			end
			if #tokens >= 1 and tokens[1]:punctuator("%") then
				if #tokens >= 2 and tokens[2]:identifier() then

					if tokens[2].value == "include" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected path")
								preprocess_fail()
							elseif not tokens[3]:stringlit() then
								tokens[3]:blamef(printf.err, "expected path")
								preprocess_fail()
							end
							if #tokens > 3 then
								tokens[4]:blamef(printf.err, "expected end of line")
								preprocess_fail()
							end
							local relative_path = tokens[3].value:gsub("^\"(.*)\"$", "%1")
							include_top = {
								path = path,
								line = line_number,
								next = include_top
							}
							include_depth = include_depth + 1
							include(path, relative_path, lines, sline)
							include_depth = include_depth - 1
							include_top = include_top.next
						end

					elseif tokens[2].value == "warning" or tokens[2].value == "error" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected message")
								preprocess_fail()
							elseif not tokens[3]:stringlit() then
								tokens[3]:blamef(printf.err, "expected message")
								preprocess_fail()
							end
							if #tokens > 3 then
								tokens[4]:blamef(printf.err, "expected end of line")
								preprocess_fail()
							end
							local err = tokens[3].value:gsub("^\"(.*)\"$", "%1")
							if tokens[2].value == "error" then
								tokens[2]:blamef(printf.err, "%%error: %s", err)
								preprocess_fail()
							else
								tokens[2]:blamef(printf.warn, "%%warning: %s", err)
							end
						end

					elseif tokens[2].value == "eval" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected alias name")
								preprocess_fail()
							elseif not tokens[3]:identifier() then
								tokens[3]:blamef(printf.err, "expected alias name")
								preprocess_fail()
							end
							local ok, result, err = evaluate(tokens, 4, #tokens, aliases)
							if not ok then
								tokens[result]:blamef(printf.err, "evaluation failed: %s", err)
								preprocess_fail()
							end
							define(tokens[3], { tokens[3]:point({
								type = "number",
								value = tostring(result)
							}) }, 1, 1)
						end

					elseif tokens[2].value == "define" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected alias name")
								preprocess_fail()
							elseif not tokens[3]:identifier() then
								tokens[3]:blamef(printf.err, "expected alias name")
								preprocess_fail()
							end
							define(tokens[3], tokens, 4, #tokens)
						end

					elseif tokens[2].value == "undef" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected alias name")
								preprocess_fail()
							elseif not tokens[3]:identifier() then
								tokens[3]:blamef(printf.err, "expected alias name")
								preprocess_fail()
							end
							if #tokens > 3 then
								tokens[4]:blamef(printf.err, "expected end of line")
								preprocess_fail()
							end
							undef(tokens[3])
						end

					elseif tokens[2].value == "if" then
						local ok, result, err = evaluate(tokens, 3, #tokens, aliases)
						if not ok then
							tokens[result]:blamef(printf.err, "evaluation failed: %s", err)
							preprocess_fail()
						end
						local evals_to_true = result ~= 0
						condition_stack[#condition_stack + 1] = {
							condition = evals_to_true,
							seen_else = false,
							been_true = evals_to_true,
							opened_by = tokens[2]
						}

					elseif tokens[2].value == "ifdef" then
						if #tokens < 3 then
							sline:blamef_after(printf.err, tokens[2], "expected alias name")
							preprocess_fail()
						elseif not tokens[3]:identifier() then
							tokens[3]:blamef(printf.err, "expected alias name")
							preprocess_fail()
						end
						if #tokens > 3 then
							tokens[4]:blamef(printf.err, "expected end of line")
							preprocess_fail()
						end
						local evals_to_true = aliases[tokens[3].value] and true
						condition_stack[#condition_stack + 1] = {
							condition = evals_to_true,
							seen_else = false,
							been_true = evals_to_true,
							opened_by = tokens[2]
						}

					elseif tokens[2].value == "ifndef" then
						if #tokens < 3 then
							sline:blamef_after(printf.err, tokens[2], "expected alias name")
							preprocess_fail()
						elseif not tokens[3]:identifier() then
							tokens[3]:blamef(printf.err, "expected alias name")
							preprocess_fail()
						end
						if #tokens > 3 then
							tokens[4]:blamef(printf.err, "expected end of line")
							preprocess_fail()
						end
						local evals_to_true = not aliases[tokens[3].value] and true
						condition_stack[#condition_stack + 1] = {
							condition = evals_to_true,
							seen_else = false,
							been_true = evals_to_true,
							opened_by = tokens[2]
						}

					elseif tokens[2].value == "else" then
						if #condition_stack == 1 then
							tokens[2]:blamef(printf.err, "unpaired %%else")
							preprocess_fail()
						end
						if condition_stack[#condition_stack].seen_else then
							tokens[2]:blamef(printf.err, "%%else after %%else")
							preprocess_fail()
						end
						condition_stack[#condition_stack].seen_else = true
						if condition_stack[#condition_stack].been_true then
							condition_stack[#condition_stack].condition = false
						else
							condition_stack[#condition_stack].condition = true
							condition_stack[#condition_stack].been_true = true
						end

					elseif tokens[2].value == "elif" then
						if #tokens > 2 then
							tokens[3]:blamef(printf.err, "expected end of line")
							preprocess_fail()
						end
						if #condition_stack == 1 then
							tokens[2]:blamef(printf.err, "unpaired %%elif")
							preprocess_fail()
						end
						if condition_stack[#condition_stack].seen_else then
							tokens[2]:blamef(printf.err, "%%elif after %%else")
							preprocess_fail()
						end
						if condition_stack[#condition_stack].been_true then
							condition_stack[#condition_stack].condition = false
						else
							local ok, result, err = evaluate(tokens, 3, #tokens, aliases)
							if not ok then
								tokens[result]:blamef(printf.err, "evaluation failed: %s", err)
								preprocess_fail()
							end
							local evals_to_true = result ~= 0
							condition_stack[#condition_stack].condition = evals_to_true
							condition_stack[#condition_stack].been_true = evals_to_true
						end

					elseif tokens[2].value == "endif" then
						if #tokens > 2 then
							tokens[3]:blamef(printf.err, "expected end of line")
							preprocess_fail()
						end
						if #condition_stack == 1 then
							tokens[2]:blamef(printf.err, "unpaired %%endif")
							preprocess_fail()
						end
						condition_stack[#condition_stack] = nil

					elseif tokens[2].value == "macro" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected macro name")
								preprocess_fail()
							elseif not tokens[3]:identifier() then
								tokens[3]:blamef(printf.err, "expected macro name")
								preprocess_fail()
							end
							if defining_macro then
								tokens[2]:blamef(printf.err, "%%macro after %%macro")
								preprocess_fail()
							end
							macro(tokens[3], tokens, 4, #tokens)
						end
						
					elseif tokens[2].value == "endmacro" then
						if condition_stack[#condition_stack].condition then
							if #tokens > 2 then
								tokens[3]:blamef(printf.err, "expected end of line")
								preprocess_fail()
							end
							if not defining_macro then
								tokens[2]:blamef(printf.err, "unpaired %%endmacro")
								preprocess_fail()
							end
							endmacro()
						end

					elseif tokens[2].value == "unmacro" then
						if condition_stack[#condition_stack].condition then
							if #tokens < 3 then
								sline:blamef_after(printf.err, tokens[2], "expected macro name")
								preprocess_fail()
							elseif not tokens[3]:identifier() then
								tokens[3]:blamef(printf.err, "expected macro name")
								preprocess_fail()
							end
							if #tokens > 3 then
								tokens[4]:blamef(printf.err, "expected end of line")
								preprocess_fail()
							end
							unmacro(tokens[3])
						end

					else
						tokens[2]:blamef(printf.err, "unknown preprocessing directive")
						preprocess_fail()

					end
				end
			else
				if condition_stack[#condition_stack].condition and #tokens > 0 then
					if defining_macro then
						table.insert(defining_macro, {
							sline = sline,
							tokens = tokens
						})
					else
						for _, line in ipairs(expand_macro(tokens, 0)) do
							table.insert(lines, line)
						end
					end
				end
			end
		end
	end

	include(false, path, lines, { blamef = function(self, report, ...)
		report(...)
	end })
	if #condition_stack > 1 then
		condition_stack[#condition_stack].opened_by:blamef(printf.err, "unfinished conditional block")
		preprocess_fail()
	end

	return lines
end

end)

require("register", "tptasm.printf", function()
local printf
printf = setmetatable({
	print = print,
	print_old = print,
	log_handle = false,
	colour = false,
	err_called = false,
	silent = false
}, { __call = function(self, ...)
	if not printf.silent then
		printf.print(string.format(...))
	end
end })

function printf.debug(from, first, ...)
	local things = { tostring(first) }
	for ix_thing, thing in ipairs({ ... }) do
		table.insert(things, tostring(thing))
	end
	printf((printf.colour and "[tptasm] " or "[tptasm] [DD] ") .. "[%s] %s", from, table.concat(things, "\t"))
end

function printf.info(format, ...)
	printf((printf.colour and "\008t[tptasm]\008w " or "[tptasm] [II] ") .. format, ...)
end

function printf.warn(format, ...)
	printf((printf.colour and "\008o[tptasm]\008w " or "[tptasm] [WW] ") .. format, ...)
end

function printf.err(format, ...)
	printf((printf.colour and "\008l[tptasm]\008w " or "[tptasm] [EE] ") .. format, ...)
	printf.err_called = true
end

function printf.redirect(log_path)
	local handle = type(log_path) == "string" and io.open(log_path, "w") or log_path
	if handle then
		printf.log_path = log_path
		printf.log_handle = handle
		printf.info("redirecting log to '%s'", tostring(log_path))
		printf.print = function(str)
			printf.log_handle:write(str .. "\n")
		end
	else
		printf.warn("failed to open '%s' for writing, log not redirected", tostring(printf.log_path))
	end
	printf.update_colour()
end

function printf.unredirect()
	if printf.log_handle then
		if type(printf.log_path) == "string" then
			printf.log_handle:close()
		end
		printf.log_handle = false
		printf.print = printf.print_old
		printf.info("undoing redirection of log to '%s'", tostring(printf.log_path))
	end
	printf.update_colour()
end

function printf.update_colour()
	printf.colour = _G.tpt and not printf.log_handle
end

function printf.failf(...)
	printf.err(...)
	error(printf.failf)
end

return printf

end)

require("register", "tptasm.resolve", function()
local printf   = require("tptasm.printf")
local config   = require("tptasm.config")
local evaluate = require("tptasm.evaluate")
local utility  = require("tptasm.utility")
local xbit32   = require("tptasm.xbit32")

local function parameters(before, expanded, first, last)
	local parameters = {}
	local parameter_buffer = {}
	local parameter_cursor = 0
	local last_comma = before
	local function flush_parameter()
		parameters[parameter_cursor] = parameter_buffer
		parameters[parameter_cursor].before = last_comma
		parameter_buffer = {}
	end
	if first <= last then
		parameter_cursor = 1
		for ix = first, last do
			if expanded[ix]:punctuator(",") then
				flush_parameter()
				last_comma = expanded[ix]
				parameter_cursor = parameter_cursor + 1
			else
				table.insert(parameter_buffer, expanded[ix])
			end
		end
		flush_parameter()
	end
	return parameters
end

local function numbers(for_length_calc, tokens)
	for ix, ix_token in ipairs(tokens) do
		if ix_token:number() then
			if not for_length_calc then
				local ok, number = ix_token:parse_number()
				if not ok then
					return false, ix, number
				end
				ix_token.parsed = number
			end
		elseif ix_token:charlit() then
			if not for_length_calc then
				local number = 0
				for first, last, code_point in utility.utf8_each(ix_token.value:gsub("^'(.*)'$", "%1")) do
					local mapped = config.litmap[code_point]
					if not mapped then
						ix_token:blamef(printf.warn, "code point %i at offset [%i, %i] not mapped, defaulting to 0", code_point, first, last)
						mapped = 0
					end
					number = xbit32.add(xbit32.lshift(number, 8), mapped)
				end
				ix_token.type = "number"
				ix_token.value = tostring(number)
				ix_token.parsed = number
			end
		end
	end
	return true
end

local function label_offsets(for_length_calc, tokens, labels, emit_rec)
	for ix, ix_token in ipairs(tokens) do
		if ix_token:identifier(config.reserved.this) then
			if for_length_calc then
				ix_token.type = "number"
				ix_token.value = "dummy"
			elseif emit_rec then
				ix_token.type = "number"
				ix_token.value = tostring(emit_rec.offset)
			end
		elseif ix_token:identifier(config.reserved.next) then
			if for_length_calc then
				ix_token.type = "number"
				ix_token.value = "dummy"
			elseif emit_rec then
				ix_token.type = "number"
				ix_token.value = tostring(emit_rec.offset + emit_rec.length)
			end
		elseif ix_token:is("label") then
			if for_length_calc then
				ix_token.type = "number"
				ix_token.value = "dummy"
			else
				local offs = labels[ix_token.value]
				if not offs then
					return false, ix, ix_token.value
				end
				ix_token.type = "number"
				ix_token.value = offs
			end
		end
	end
	return true
end

local function evaluations(for_length_calc, tokens, labels, emit_rec)
	for ix, ix_token in ipairs(tokens) do
		if ix_token:is("evaluation") then
			if for_length_calc then
				ix_token.type = "number"
				ix_token.value = "dummy"
			else
				local labels_ok, jx, err = label_offsets(for_length_calc, ix_token.value, labels, emit_rec)
				if labels_ok then
					local ok, result, err = evaluate(ix_token.value, 1, #ix_token.value, {})
					if ok then
						ix_token.type = "number"
						ix_token.value = tostring(result)
					else
						return false, ix, result, err
					end
				else
					return false, ix, jx, err
				end
			end
		end
	end
	return true
end


local function instructions(architecture, lines)
	local label_context = {}
	local output_pointer = 0
	local to_emit = {}
	local labels = {}
	local model_restrictions = {}

	local hooks = {}
	hooks[config.reserved.org] = function(hook_token, parameters)
		if #parameters < 1 then
			hook_token:blamef_after(printf.err, "expected origin")
			return false
		end
		if #parameters > 1 then
			parameters[1][#parameters[1]]:blamef_after(printf.err, "excess parameters")
			return false
		end
		local org_pack = parameters[1]
		if #org_pack > 1 then
			org_pack[2]:blamef(printf.err, "excess tokens")
			return false
		end
		local org = org_pack[1]
		if not org:is("number") then
			org:blamef(printf.err, "not a number")
			return false
		end
		output_pointer = org.parsed
		return true
	end
	hooks[config.reserved.dw] = function(hook_token, parameters)
		-- * TODO: allow higher shifts, currently dw constants are truncated
		--         to 32 bits. not sure how to get around this
		for _, ix_param in ipairs(parameters) do
			if #ix_param < 1 then
				ix_param.before:blamef_after(printf.err, "no tokens")
				return false
			end
			if #ix_param == 1 and ix_param[1]:stringlit() then
				local values = {}
				for first, last, code_point in utility.utf8_each(ix_param[1].value:gsub("^\"(.*)\"$", "%1")) do
					local mapped = config.litmap[code_point]
					if not mapped then
						ix_param[1]:blamef(printf.warn, "code point %i at offset [%i, %i] not mapped, defaulting to 0", code_point, first, last)
						mapped = 0
					end
					table.insert(values, architecture.nop:clone():merge(mapped, 0))
				end
				to_emit[output_pointer] = {
					emit = values,
					length = #values,
					emitted_by = ix_param[1],
					offset = output_pointer
				}
				to_emit[output_pointer].head = to_emit[output_pointer]
				output_pointer = output_pointer + #values
			else
				to_emit[output_pointer] = {
					emit = function()
						if #ix_param > 1 then
							ix_param[2]:blamef(printf.err, "excess tokens")
							return false
						end
						if not ix_param[1]:number() then
							ix_param[1]:blamef(printf.err, "expected string literal or number")
							return false
						end
						local number = ix_param[1].parsed
						if number >= 2 ^ architecture.dw_bits then
							number = number % 2 ^ architecture.dw_bits
							ix_param[1]:blamef(printf.warn, "number truncated to %i bits", architecture.dw_bits)
						end
						return true, { architecture.nop:clone():merge(number, 0) }
					end,
					parameters = { ix_param },
					length = 1,
					emitted_by = ix_param[1],
					offset = output_pointer
				}
				to_emit[output_pointer].head = to_emit[output_pointer]
				output_pointer = output_pointer + 1
			end
		end
		return true
	end
	hooks[config.reserved.litmap] = function(hook_token, parameters)
		if #parameters == 0 then
			hook_token:blamef_after(printf.err, "expected base")
			return false
		end
		if #parameters[1] < 1 then
			parameters[1]:blamef_after(printf.err, "no tokens")
			return false
		end
		if #parameters[1] > 1 then
			parameters[1][2]:blamef(printf.err, "excess tokens")
			return false
		end
		if not parameters[1][1]:number() then
			parameters[1][1]:blamef(printf.err, "not a number")
			return false
		end
		if #parameters == 1 then
			parameters[1][1]:blamef_after(printf.err, "expected map")
			return false
		end
		if #parameters[2] < 1 then
			parameters[2]:blamef_after(printf.err, "no tokens")
			return false
		end
		if #parameters[2] > 1 then
			parameters[2][2]:blamef(printf.err, "excess tokens")
			return false
		end
		if not parameters[2][1]:stringlit() then
			parameters[2][1]:blamef(printf.err, "not a string literal")
			return false
		end
		local base = parameters[1][1].parsed
		local map = parameters[2][1].value:gsub("^\"(.*)\"$", "%1")
		local counter = 0
		for first, last, code_point in utility.utf8_each(map) do
			config.litmap[code_point] = base + counter
			counter = counter + 1
		end
		return true
	end
	hooks[config.reserved.model] = function(hook_token, parameters)
		if #parameters < 1 then
			hook_token:blamef_after(printf.err, "expected models")
			return false
		end
		for _, model_pack in ipairs(parameters) do
			if #model_pack < 1 then
				model_pack[2].before:blamef_after(printf.err, "no tokens")
				return false
			elseif #model_pack > 1 then
				model_pack[2]:blamef(printf.err, "excess tokens")
				return false
			end
			local model = model_pack[1]
			if not model:is("stringlit") then
				model:blamef(printf.err, "not a string literal")
				return false
			end
			local restrictions = model_restrictions[model.sline.path]
			if not restrictions then
				restrictions = {}
				model_restrictions[model.sline.path] = restrictions
			end
			table.insert(restrictions, model)
		end
		return true
	end

	local reserved_set = {}
	for _, item in pairs(config.reserved) do
		reserved_set[item] = true
	end
	local function known_identifiers(name)
		return (architecture.entities[name]
		     or architecture.mnemonics[name]
		     or hooks[name]
		     or reserved_set[name]) and true
	end

	for _, tokens in ipairs(lines) do
		local line_failed = false

		if not line_failed then
			local cursor = #tokens
			while cursor >= 1 do
				if tokens[cursor]:stringlit() then
					while cursor > 1 and tokens[cursor - 1]:stringlit() do
						tokens[cursor - 1] = tokens[cursor - 1]:point({
							type = "stringlit",
							value = tokens[cursor - 1].value .. tokens[cursor].value
						})
						table.remove(tokens, cursor)
						cursor = cursor - 1
					end

				elseif tokens[cursor]:charlit() then
					while cursor > 1 and tokens[cursor - 1]:charlit() do
						tokens[cursor - 1] = tokens[cursor - 1]:point({
							type = "charlit",
							value = tokens[cursor - 1].value .. tokens[cursor].value
						})
						table.remove(tokens, cursor)
						cursor = cursor - 1
					end

				elseif tokens[cursor]:identifier() and architecture.entities[tokens[cursor].value] then
					tokens[cursor] = tokens[cursor]:point({
						type = "entity",
						value = tokens[cursor].value,
						entity = architecture.entities[tokens[cursor].value]
					})

				elseif tokens[cursor]:identifier() and architecture.mnemonics[tokens[cursor].value] then
					tokens[cursor] = tokens[cursor]:point({
						type = "mnemonic",
						value = tokens[cursor].value,
						mnemonic = architecture.mnemonics[tokens[cursor].value]
					})

				elseif tokens[cursor]:identifier() and hooks[tokens[cursor].value] then
					tokens[cursor] = tokens[cursor]:point({
						type = "hook",
						value = tokens[cursor].value,
						hook = hooks[tokens[cursor].value]
					})

				elseif (tokens[cursor]:identifier() and not known_identifiers(tokens[cursor].value)) or
					   (tokens[cursor]:identifier(config.reserved.labelcontext)) then
					while cursor > 1 do
						if tokens[cursor - 1]:identifier() and not known_identifiers(tokens[cursor - 1].value) then
							tokens[cursor - 1] = tokens[cursor - 1]:point({
								type = "identifier",
								value = tokens[cursor - 1].value .. tokens[cursor].value
							})
							table.remove(tokens, cursor)
							cursor = cursor - 1

						elseif tokens[cursor - 1]:identifier(config.reserved.peerlabel) then
							if #label_context < 1 then
								tokens[cursor - 1]:blamef(printf.err, "peer-label reference in level %i context", #label_context - 1)
								line_failed = true
								break
							end
							tokens[cursor - 1] = tokens[cursor - 1]:point({
								type = "identifier",
								value = ("."):rep(#label_context - 1) .. tokens[cursor].value
							})
							table.remove(tokens, cursor)
							cursor = cursor - 1

						elseif tokens[cursor - 1]:identifier(config.reserved.superlabel) then
							if #label_context < 2 then
								tokens[cursor - 1]:blamef(printf.err, "super-label reference in level %i context", #label_context - 1)
								line_failed = true
								break
							end
							tokens[cursor - 1] = tokens[cursor - 1]:point({
								type = "identifier",
								value = ("."):rep(#label_context - 2) .. tokens[cursor].value
							})
							table.remove(tokens, cursor)
							cursor = cursor - 1

						elseif tokens[cursor - 1]:punctuator(".") then
							tokens[cursor - 1] = tokens[cursor - 1]:point({
								type = "identifier",
								value = "." .. tokens[cursor].value
							})
							table.remove(tokens, cursor)
							cursor = cursor - 1

						else
							break

						end
					end

					if not line_failed then
						local dots, rest = tokens[cursor].value:match("^(%.*)(.+)$")
						local level = #dots
						if level > #label_context then
							tokens[cursor]:blamef(printf.err, "level %i label declaration without preceding level %i label declaration", level, level - 1)
							line_failed = true
							break
						else
							local name_tbl = {}
							for ix = 1, level do
								table.insert(name_tbl, label_context[ix])
							end
							table.insert(name_tbl, rest)
							tokens[cursor] = tokens[cursor]:point({
								type = "label",
								value = table.concat(name_tbl, "."),
								ignore = rest == config.reserved.labelcontext,
								level = level,
								rest = rest
							})
						end
					end

				end
				cursor = cursor - 1
			end
		end

		if not line_failed then
			local cursor = 1
			while cursor <= #tokens do
				if tokens[cursor]:punctuator("{") then
					local brace_end = cursor + 1
					local last
					while brace_end <= #tokens do
						if tokens[brace_end]:punctuator("}") then
							last = brace_end
							break
						end
						brace_end = brace_end + 1
					end
					if not last then
						tokens[cursor]:blamef(printf.err, "unfinished evaluation block")
						line_failed = true
						break
					end
					local eval_tokens = {}
					for ix = cursor + 1, last - 1 do
						table.insert(eval_tokens, tokens[ix])
					end
					for _ = cursor + 1, last do
						table.remove(tokens, cursor + 1)
					end
					tokens[cursor].type = "evaluation"
					tokens[cursor].value = eval_tokens
				end
				cursor = cursor + 1
			end
		end

		if not line_failed then
			while #tokens >= 2 and tokens[1]:is("label") and tokens[2]:punctuator(":") do
				if tokens[1].ignore then
					for ix = tokens[1].level + 2, #label_context do
						label_context[ix] = nil
					end
				else
					for ix = tokens[1].level + 1, #label_context do
						label_context[ix] = nil
					end
					labels[tokens[1].value] = tostring(output_pointer)
					label_context[tokens[1].level + 1] = tokens[1].rest
				end
				table.remove(tokens, 1)
				table.remove(tokens, 1)
			end

			if #tokens >= 1 and tokens[1]:is("mnemonic") then
				local funcs = tokens[1].mnemonic
				local params = parameters(tokens[1], tokens, 2, #tokens)
				local params_flc = {}
				for px = 1, #params do
					local param = params[px]
					local param_flc = {}
					for tx = 1, #param do
						table.insert(param_flc, param[tx]:clone())
					end
					label_offsets(true, param_flc)
					evaluations(true, param_flc)
					numbers(true, param_flc)
					table.insert(params_flc, param_flc)
				end
				local ok, length = funcs.length(tokens[1], params_flc)
				if ok then
					local overwrites = {}
					for ix = output_pointer, output_pointer + length - 1 do
						local overwritten = to_emit[ix]
						if overwritten then
							overwrites[overwritten.head] = true
						end
					end
					if next(overwrites) then
						local overwritten_count = 0
						for _ in pairs(overwrites) do
							overwritten_count = overwritten_count + 1
						end
						tokens[1]:blamef(printf.warn, "opcode emitted here (offs 0x%04X, size %i) overwrites the following %i opcodes:", output_pointer, length, overwritten_count)
						for overwritten in pairs(overwrites) do
							overwritten.emitted_by:blamef(printf.info, "opcode emitted here (offs 0x%04X, size %i)", overwritten.offset, overwritten.length)
						end
					end
					to_emit[output_pointer] = {
						emit = funcs.emit,
						parameters = params,
						length = length,
						emitted_by = tokens[1],
						offset = output_pointer
					}
					to_emit[output_pointer].head = to_emit[output_pointer]
					for ix = output_pointer + 1, output_pointer + length - 1 do
						to_emit[ix] = {
							head = to_emit[output_pointer]
						}
					end
					output_pointer = output_pointer + length
				else
					line_failed = true
				end

			elseif #tokens >= 1 and tokens[1]:is("hook") then
				local parameters = parameters(tokens[1], tokens, 2, #tokens)
				for ix, ix_param in ipairs(parameters) do
					local evals_ok, ix, jx, err = evaluations(false, ix_param, labels)
					if evals_ok then
						local numbers_ok, ix, err = numbers(false, ix_param)
						if not numbers_ok then
							ix_param[ix]:blamef(printf.err, "invalid number: %s", err)
							line_failed = true
						end
					else
						ix_param[ix].value[jx]:blamef(printf.err, "evaluation failed: %s", err)
						line_failed = true
					end
				end
				if not line_failed then
					line_failed = not tokens[1].hook(tokens[1], parameters)
				end

			elseif #tokens == 0 then
				-- * Nothing.

			else
				tokens[1]:blamef(printf.err, "expected label declaration, instruction or hook invocation")
				line_failed = true

			end
		end

		if line_failed then
			printf.err_called = true
		end
	end
	if printf.err_called then
		printf.failf("instruction resolution stage failed, bailing")
	end

	return to_emit, labels, model_restrictions
end

return {
	parameters = parameters,
	numbers = numbers,
	label_offsets = label_offsets,
	evaluations = evaluations,
	instructions = instructions,
}

end)

require("register", "tptasm.tokenise", function()
local printf = require("tptasm.printf")

local token_i = {}
local token_mt = { __index = token_i }

function token_i:is(type, value)
	return self.type == type and (not value or self.value == value)
end

function token_i:punctuator(...)
	return self:is("punctuator", ...)
end

function token_i:identifier(...)
	return self:is("identifier", ...)
end

function token_i:stringlit(...)
	return self:is("stringlit", ...)
end

function token_i:charlit(...)
	return self:is("charlit", ...)
end

function token_i:number()
	return self:is("number")
end

local function parse_number_base(str, base)
	local out = 0
	for ix, ch in str:gmatch("()(.)") do
		local pos = base:find(ch)
		if not pos then
			return false, ("invalid digit at position %i"):format(ix)
		end
		out = out * #base + (pos - 1)
		if out >= 0x100000000 then
			return false, "unsigned 32-bit overflow"
		end
	end
	return true, out
end

function token_i:parse_number()
	local str = self.value
	if str:match("^0[Xx][0-9A-Fa-f]+$") then
		return parse_number_base(str:sub(3):lower(), "0123456789abcdef")
	elseif str:match("^[0-9A-Fa-f]+[Hh]$") then
		return parse_number_base(str:sub(1, -2), "0123456789abcdef")
	elseif str:match("^0[Bb][0-1]+$") then
		return parse_number_base(str:sub(3), "01")
	elseif str:match("^0[Oo][0-7]+$") then
		return parse_number_base(str:sub(3), "01234567")
	elseif str:match("^[0-9]+$") then
		return parse_number_base(str, "0123456789")
	end
	return false, "notation not recognised"
end

function token_i:point(other)
	other.sline = self.sline
	other.soffs = self.soffs
	other.expanded_from = self.expanded_from
	return setmetatable(other, token_mt)
end

function token_i:blamef_after(report, format, ...)
	self.sline:blamef_after(report, self, format, ...)
end

function token_i:blamef(report, format, ...)
	report("%s:%i:%i: " .. format, self.sline.path, self.sline.line, self.soffs, ...)
	self.sline:dump_itop()
	if self.expanded_from then
		self.expanded_from:blamef(printf.info, "  expanded from this")
	end
end

function token_i:clone()
	local clone = setmetatable({}, token_mt)
	for key, value in pairs(self) do
		clone[key] = value
	end
	return clone
end

function token_i:expand_by(other)
	local clone = self:clone()
	clone.expanded_from = other
	return clone
end

local transition = {}
local all_8bit = ""
for ix = 0, 255 do
	all_8bit = all_8bit .. string.char(ix)
end
local function transitions(transition_list)
	local tbl = {}
	local function add_transition(cond, action)
		if type(cond) == "string" then
			for ch in all_8bit:gmatch(cond) do
				tbl[ch:byte()] = action
			end
		else
			tbl[cond] = action
		end
	end
	for _, ix_trans in ipairs(transition_list) do
		add_transition(ix_trans[1], ix_trans[2])
	end
	return tbl
end

transition.push = transitions({
	{         "'", { consume =  true, state = "charlit"    }},
	{        "\"", { consume =  true, state = "stringlit"  }},
	{     "[;\n]", { consume = false, state = "done"       }},
	{     "[0-9]", { consume =  true, state = "number"     }},
	{ "[_A-Za-z]", { consume =  true, state = "identifier" }},
	{ "[%[%]%(%)%+%-%*/%%:%?&#<>=!^~%.{}\\|@$,`]", { consume = false, state = "punctuator" }},
})
transition.identifier = transitions({
	{ "[_A-Za-z0-9]", { consume =  true, state = "identifier" }},
	{          false, { consume = false, state = "push"       }},
})
transition.number = transitions({
	{ "[_A-Za-z0-9]", { consume =  true, state = "number" }},
	{          false, { consume = false, state = "push"   }},
})
transition.charlit = transitions({
	{  "'", { consume = true, state = "push"         }},
	{ "\n", { error = "unfinished character literal" }},
})
transition.stringlit = transitions({
	{ "\"", { consume = true, state = "push"      }},
	{ "\n", { error = "unfinished string literal" }},
})
transition.punctuator = transitions({
	{ ".", { consume = true, state = "push" }},
})

local whitespace = {
	[("\f"):byte()] = true,
	[("\n"):byte()] = true,
	[("\r"):byte()] = true,
	[("\t"):byte()] = true,
	[("\v"):byte()] = true,
	[(" " ):byte()] = true
}

return function(sline)
	local line = sline.str .. "\n"
	local tokens = {}
	local state = "push"
	local token_begin
	local cursor = 1
	while cursor <= #line do
		local ch = line:byte(cursor)
		if state == "push" and whitespace[ch] and #tokens > 0 then
			tokens[#tokens].whitespace_follows = true
		end
		local old_state = state
		local transition_info = transition[state][ch] or transition[state][false]
		local consume = true
		if transition_info then
			if transition_info.error then
				return false, cursor, transition_info.error
			end
			state = transition_info.state
			consume = transition_info.consume
		end
		if consume then
			cursor = cursor + 1
		end
		if state == "done" then
			break
		end
		if old_state == "push" and state ~= "push" then
			token_begin = cursor
			if consume then
				token_begin = token_begin - 1
			end
		end
		if old_state ~= "push" and state == "push" then
			local token_end = cursor - 1
			table.insert(tokens, setmetatable({
				type = old_state,
				value = line:sub(token_begin, token_end),
				sline = sline,
				soffs = token_begin
			}, token_mt))
		end
	end
	if #tokens > 0 then
		tokens[#tokens].whitespace_follows = true
	end
	return true, tokens
end

end)

require("register", "tptasm.utility", function()
local printf = require("tptasm.printf")

local function get_line(up)
	local _, err = pcall(error, "@", up + 2)
	return err:match("^(.-)%s*:%s*@$")
end

local function parse_args(args)
	local named_args = {}
	local unnamed_args = {}
	if #args == 1 and type(args[1]) == "table" then
		for _, arg in ipairs(args[1]) do
			table.insert(unnamed_args, arg)
		end
		for key, arg in pairs(args[1]) do
			if type(key) ~= "number" then
				named_args[key] = arg
			end
		end
	else
		local max_arg = 0
		for key in pairs(args) do
			max_arg = key
		end
		local unnamed_counter = 0
		for ix_arg = 1, max_arg do
			local arg = args[ix_arg]
			local key_value = type(arg) == "string" and { arg:match("^([^=]+)=(.+)$") }
			if key_value and key_value[1] then
				if named_args[key_value[1]] then
					printf.warn("argument #%i overrides earlier specification of %s", ix_arg, key_value[1])
				end
				named_args[key_value[1]] = key_value[2]
			else
				unnamed_counter = unnamed_counter + 1
				unnamed_args[unnamed_counter] = arg
			end
		end
	end
	return named_args, unnamed_args
end

local function resolve_relative(base_with_file, relative)
	local components = {}
	local parent_depth = 0
	-- * TODO: support more prefixes (i.e. C:, eww)
	local prefix, concatenated_path = (base_with_file .. "/../" .. relative):match("^(/?)(.+)$")
	for component in concatenated_path:gmatch("[^/]+") do
		if component == ".." then
			if #components > 0 then
				components[#components] = nil
			else
				parent_depth = parent_depth + 1
			end
		elseif component ~= "." then
			table.insert(components, component)
		end
	end
	for _ = 1, parent_depth do
		table.insert(components, 1, "..")
	end
	return prefix .. table.concat(components, "/")
end

local function utf8_each(str)
	local cursor = 0
	return function()
		if cursor >= #str then
			return
		end
		cursor = cursor + 1
		local head = str:byte(cursor)
		if head < 0x80 then
			return cursor, cursor, head
		end
		if head < 0xE0 and cursor + 1 <= #str then
			local cont1 = str:byte(cursor + 1, cursor + 1)
			cursor = cursor + 1
			return cursor - 1, cursor, head % 0x20 * 0x40 + cont1 % 0x40
		end
		if head < 0xF0 and cursor + 2 <= #str then
			local cont1, cont2 = str:byte(cursor + 1, cursor + 2)
			cursor = cursor + 2
			return cursor - 2, cursor, head % 0x10 * 0x1000 + cont1 % 0x40 * 0x40 + cont2 % 0x40
		end
		if head < 0xF8 and cursor + 3 <= #str then
			local cont1, cont2, cont3 = str:byte(cursor + 1, cursor + 3)
			cursor = cursor + 3
			return cursor - 3, cursor, head % 0x08 * 0x40000 + cont1 % 0x40 * 0x1000 + cont2 % 0x40 * 0x40 + cont3 % 0x40
		end
	end
end

return {
	get_line = get_line,
	parse_args = parse_args,
	resolve_relative = resolve_relative,
	utf8_each = utf8_each,
}

end)

require("register", "tptasm.xbit32", function()
local function sub(a, b)
	local s = a - b
	if s < 0 then
		s = s + 0x100000000
	end
	return s
end

local function add(a, b)
	local s = a + b
	if s >= 0x100000000 then
		s = s - 0x100000000
	end
	return s
end

local function divmod(a, b)
	local quo = math.floor(a / b)
	return quo, a - quo * b
end

local function div(a, b)
	local quo, rem = divmod(a, b)
	return quo
end

local function mod(a, b)
	local quo, rem = divmod(a, b)
	return rem
end

local function hasbit(a, b)
	return a % (b + b) >= b
end

local function band(a, b)
	local curr = 1
	local out = 0
	for ix = 0, 31 do
		if hasbit(a, curr) and hasbit(b, curr) then
			out = out + curr
		end
		curr = curr * 2
	end
	return out
end

local function mul(a, b)
	local ll = band(a, 0xFFFF) * band(b, 0xFFFF)
	local lh = band(band(a, 0xFFFF) * math.floor(b / 0x10000), 0xFFFF)
	local hl = band(math.floor(a / 0x10000) * band(b, 0xFFFF), 0xFFFF)
	return add(add(ll, lh * 0x10000), hl * 0x10000)
end

local function bor(a, b)
	local curr = 1
	local out = 0
	for ix = 0, 31 do
		if hasbit(a, curr) or hasbit(b, curr) then
			out = out + curr
		end
		curr = curr * 2
	end
	return out
end

local function bxor(a, b)
	local curr = 1
	local out = 0
	for ix = 0, 31 do
		if hasbit(a, curr) ~= hasbit(b, curr) then
			out = out + curr
		end
		curr = curr * 2
	end
	return out
end

local function lshift(a, b)
	if b >= 32 then
		return 0
	end
	return mul(a, 2 ^ b)
end

local function rshift(a, b)
	if b >= 32 then
		return 0
	end
	return div(a, 2 ^ b)
end

return {
	bxor = bxor,
	bor = bor,
	band = band,
	sub = sub,
	add = add,
	mul = mul,
	div = div,
	mod = mod,
	rshift = rshift,
	lshift = lshift,
}

end)

return require("run", "tptasm")

Description:

Changelog: