diff --git a/Lua/inject.lua b/Lua/inject.lua index 7454589..0acaee1 100644 --- a/Lua/inject.lua +++ b/Lua/inject.lua @@ -107,5 +107,5 @@ end if oot then inject('spawn oot.asm') else - inject('spawn.asm') + inject('spawn mm.asm') end diff --git a/Lua/inject/lips.lua b/Lua/inject/lips.lua index d6d602a..4638f4c 100644 --- a/Lua/inject/lips.lua +++ b/Lua/inject/lips.lua @@ -33,6 +33,16 @@ local function bitrange(x, lower, upper) return floor(x/2^lower) % 2^(upper - lower + 1) end +local function readfile(fn) + local f = io.open(fn, 'r') + if not f then + error('could not open assembly file for reading: '..tostring(fn), 2) + end + local asm = f:read('*a') + f:close() + return asm +end + local registers = { [0]= 'R0', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3', @@ -446,9 +456,10 @@ end revtable(all_instructions) local Lexer = Class() -function Lexer:init(asm, fn) +function Lexer:init(asm, fn, options) self.asm = asm self.fn = fn or '(string)' + self.options = options or {} self.pos = 1 self.line = 1 self.EOF = -1 @@ -459,21 +470,22 @@ local Dumper = Class() function Dumper:init(writer, fn, options) self.writer = writer self.fn = fn or '(string)' + self.options = options or {} self.labels = {} self.commands = {} self.buff = '' self.pos = 0 self.size = 0 self.lastcommand = nil - self.options = options or {} end local Parser = Class() function Parser:init(writer, fn, options) self.fn = fn or '(string)' + self.main_fn = self.fn + self.options = options or {} self.dumper = Dumper(writer, fn, options) self.defines = {} - self.options = options or {} end function Lexer:error(msg) @@ -655,7 +667,39 @@ function Lexer:lex_block_comment(yield) end end -function Lexer:lex(yield) +function Lexer:lex_string(yield) + -- TODO: support escaping + if self.chr ~= '"' then + print(self.chr, self.ord) + self:error("expected opening double quote") + end + self:nextc() + local buff = self:read_chars('[^"\n]') + if self.chr ~= '"' then + print(self.chr) + self:error("expected closing double quote") + end + self:nextc() + yield('STRING', buff) +end + +function Lexer:lex_include(_yield) + self:read_chars('%s') + local fn + self:lex_string(function(tt, tok) + fn = tok + end) + if self.options.path then + fn = self.options.path..fn + end + local sublexer = Lexer(readfile(fn), fn, self.options) + sublexer:lex(_yield) +end + +function Lexer:lex(_yield) + local function yield(tt, tok) + return _yield(tt, tok, self.fn) + end while true do if self.chr == '\n' then self:nextc() @@ -709,6 +753,7 @@ function Lexer:lex(yield) end if up == 'INC' or up == 'INCASM' or up == 'INCLUDE' then yield('DIR', 'INC') + self:lex_include(_yield) else yield('DIR', up) end @@ -769,16 +814,11 @@ function Parser:advance() local t = self.tokens[self.i] self.tt = t.tt self.tok = t.tok + self.fn = t.fn self.line = t.line return t.tt, t.tok end -function Parser:lookahead() - local t = self.tokens[self.i] + 1 - if t == nil then return end - return t.tt, t.tok -end - function Parser:is_EOL() return self.tt == 'EOL' or self.tt == 'EOF' end @@ -837,7 +877,9 @@ function Parser:directive() self:expect_EOL() elseif name == 'HEX' then self:error('unimplemented') - elseif name == 'INC' or name == 'INCBIN' then + elseif name == 'INC' then + -- noop + elseif name == 'INCBIN' then self:error('unimplemented') elseif name == 'FLOAT' or name == 'ASCII' or name == 'ASCIIZ' then self:error('unimplemented') @@ -1211,27 +1253,28 @@ function Parser:instruction() self:expect_EOL() end -function Parser:tokenize() +function Parser:tokenize(asm) self.tokens = {} self.i = 0 local line = 1 local routine = coroutine.create(function() - local lexer = Lexer(self.asm, self.fn) + local lexer = Lexer(asm, self.main_fn, self.options) lexer:lex(coroutine.yield) end) - local lex = function() + local function lex() local t = {line=line} - local ok, a, b = coroutine.resume(routine) + local ok, a, b, c = coroutine.resume(routine) if not ok then a = a or 'Internal Error: lexer coroutine has stopped' error(a) end t.tt = a t.tok = b + t.fn = c table.insert(self.tokens, t) - return t.tt, t.tok + return t.tt, t.tok, t.fn end -- first pass: collect tokens and constants. @@ -1241,7 +1284,7 @@ function Parser:tokenize() -- this would cause a recursive problem to solve, -- which is too much for our simple assembler. while true do - local tt, tok = lex() + local tt, tok, fn = lex() if tt == 'DEF' then local tt2, tok2 = lex() if tt2 ~= 'NUM' then @@ -1251,8 +1294,11 @@ function Parser:tokenize() elseif tt == 'EOL' then line = line + 1 elseif tt == 'EOF' then - break + if fn == self.main_fn then + break + end elseif tt == nil then + --require("pt"){self.tokens, writer=print} error('Internal Error: missing token', 1) end end @@ -1271,12 +1317,14 @@ function Parser:tokenize() end function Parser:parse(asm) - self.asm = asm - self:tokenize() + self:tokenize(asm) self:advance() while true do if self.tt == 'EOF' then - break + if self.fn == self.main_fn then + break + end + self:advance() elseif self.tt == 'EOL' then -- empty line self:advance() @@ -1354,7 +1402,6 @@ function Dumper:add_bytes(line, ...) end function Dumper:add_directive(line, name, a, b) - -- ORG ALIGN SKIP BYTE HALFWORD WORD local t = {} t.line = line if name == 'BYTE' then @@ -1585,17 +1632,13 @@ function assembler.assemble(fn_or_asm, writer, options) function main() local fn = nil - local asm = '' + local asm if fn_or_asm:find('[\r\n]') then asm = fn_or_asm else fn = fn_or_asm - local f = io.open(fn, 'r') - if not f then - error('could not read assembly file', 1) - end - asm = f:read('*a') - f:close() + asm = readfile(fn) + options.path = fn:match(".*/") end local parser = Parser(writer, fn, options) diff --git a/Lua/inject/spawn mm.asm b/Lua/inject/spawn mm.asm new file mode 100644 index 0000000..2077ce7 --- /dev/null +++ b/Lua/inject/spawn mm.asm @@ -0,0 +1,19 @@ +[actor_spawn]: 0x800BAE14 +[max_actor_no]: 0x2B1 + +[global_context]: 0x803E6B20 +[buttons_offset]: 0x14 +[actor_spawn_offset]: 0x1CA0 + +[link_actor]: 0x803FFDB0 +[actor_x]: 0x24 +[actor_y]: 0x28 +[actor_z]: 0x2C +[actor_horiz_angle]: 0x32 + +[link_save]: 0x801EF670 +[rupees_offset]: 0x3A +[upgrades_offset]: 0xB8 +[upgrades_2_offset]: 0xBA + +.include "spawn.asm" diff --git a/Lua/inject/spawn oot.asm b/Lua/inject/spawn oot.asm index 62f2deb..2178fc2 100644 --- a/Lua/inject/spawn oot.asm +++ b/Lua/inject/spawn oot.asm @@ -16,112 +16,4 @@ [upgrades_offset]: 0xA0 [upgrades_2_offset]: 0xA2 -[button_L]: 0x0020 -[button_D_right]: 0x0100 -[button_D_left]: 0x0200 -[button_D_down]: 0x0400 -[button_D_up]: 0x0800 -[button_any]: 0x0F20 - -[hold_delay_amount]: 3 - - push 4, s1, ra - li t0, @link_save - li t1, @global_context -// give max rupee upgrade (set bit 13, clear bit 12 of lower halfword) - lh t2, @upgrades_2_offset(t0) - ori t2, t2, 0x2000 - andi t2, t2, 0xEFFF - sh t2, @upgrades_2_offset(t0) -// - lhu t2, @buttons_offset(t1) - lh t9, @rupees_offset(t0) - lw s1, hold_delay - andi t4, t2, @button_any - bne t4, r0, no_reset - addi s1, s1, 1 - li s1, 0 -no_reset: - subi t4, s1, 1 - beq t4, r0, first_time - nop - subi t4, s1, @hold_delay_amount - bltz t4, return - nop -first_time: - andi t3, t2, @button_D_up - beq t3, r0, no_D_up - nop - addi t9, t9, 1 -no_D_up: - andi t3, t2, @button_D_down - beq t3, r0, no_D_down - nop - subi t9, t9, 1 -no_D_down: - andi t3, t2, @button_D_right - beq t3, r0, no_D_right - nop - addi t9, t9, 10 -no_D_right: - andi t3, t2, @button_D_left - beq t3, r0, no_D_left - nop - subi t9, t9, 10 -no_D_left: - subi t4, t9, 1 - bgez t4, no_min - nop - li t9, @max_actor_no -no_min: - subi t4, t9, @max_actor_no - blez t4, no_max - nop - li t9, 1 -no_max: - sh t9, @rupees_offset(t0) - andi t3, t2, @button_L - beq t3, r0, return - nop - mov a0, t9 - bal simple_spawn - nop -return: - sw s1, hold_delay - jpop 4, s1, ra - -simple_spawn: // args: a0 (actor to spawn) - push 4, 9, ra - mov a2, a0 - li a1, @global_context - addi a0, a1, @actor_spawn_offset - li t0, @link_actor - lw t1, @actor_x(t0) - lw t2, @actor_y(t0) - lw t3, @actor_z(t0) - mov a3, t1 // X position - sw t2, 0x10(sp) // Y position - sw t3, 0x14(sp) // Z position - - li t9, 0x0 - sw t9, 0x18(sp) // rotation? - lh t7, @actor_horiz_angle(t0) - sw t7, 0x1C(sp) // horizontal rotation - li t9, 0x0 - sw t9, 0x20(sp) // rotation? - - lh t7, @actor_horiz_angle(t0) - sw t7, 0x24(sp) // actor variable - - li t9, 0x0000007F - sw t9, 0x28(sp) // unknown - li t9, 0x000003FF - sw t9, 0x2C(sp) // unknown - li t9, 0x00000000 - sw t9, 0x30(sp) // unknown - jal @actor_spawn - nop - jpop 4, 9, ra - -hold_delay: - .word 0 +.include "spawn.asm" diff --git a/Lua/inject/spawn.asm b/Lua/inject/spawn.asm index 0d58c90..ed67999 100644 --- a/Lua/inject/spawn.asm +++ b/Lua/inject/spawn.asm @@ -1,21 +1,3 @@ -[actor_spawn]: 0x800BAE14 -[max_actor_no]: 0x2B1 - -[global_context]: 0x803E6B20 -[buttons_offset]: 0x14 -[actor_spawn_offset]: 0x1CA0 - -[link_actor]: 0x803FFDB0 -[actor_x]: 0x24 -[actor_y]: 0x28 -[actor_z]: 0x2C -[actor_horiz_angle]: 0x32 - -[link_save]: 0x801EF670 -[rupees_offset]: 0x3A -[upgrades_offset]: 0xB8 -[upgrades_2_offset]: 0xBA - [button_L]: 0x0020 [button_D_right]: 0x0100 [button_D_left]: 0x0200