diff --git a/Lua/lib/lips/Collector.lua b/Lua/lib/lips/Collector.lua index 2fb21a7..b258186 100644 --- a/Lua/lib/lips/Collector.lua +++ b/Lua/lib/lips/Collector.lua @@ -108,12 +108,12 @@ function Collector:directive() if self:is_EOL() and name == 'ALIGN' then add(name) else - local size = self:number() + local size = self:const(nil, 'no label') if self:is_EOL() then add(name, size) else self:optional_comma() - add(name, size, self:number()) + add(name, size, self:const(nil, 'no label')) end end elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then diff --git a/Lua/lib/lips/Dumper.lua b/Lua/lib/lips/Dumper.lua index 6a7c0db..058d4d4 100644 --- a/Lua/lib/lips/Dumper.lua +++ b/Lua/lib/lips/Dumper.lua @@ -139,40 +139,23 @@ function Dumper:format_in(informat) for i=1, #informat do self.i = i local c = informat:sub(i, i) - if c == 'd' and not args.rd then - args.rd = self:register(data.registers) - elseif c == 's' and not args.rs then - args.rs = self:register(data.registers) - elseif c == 't' and not args.rt then - args.rt = self:register(data.registers) - elseif c == 'D' and not args.fd then - args.fd = self:register(data.fpu_registers) - elseif c == 'S' and not args.fs then - args.fs = self:register(data.fpu_registers) - elseif c == 'T' and not args.ft then - args.ft = self:register(data.fpu_registers) - elseif c == 'X' and not args.rd then - args.rd = self:register(data.sys_registers) - elseif c == 'Y' and not args.rs then - args.rs = self:register(data.sys_registers) - elseif c == 'Z' and not args.rt then - args.rt = self:register(data.sys_registers) - elseif c == 'o' and not args.offset then - args.offset = self:const():set('signed') - elseif c == 'r' and not args.offset then - args.offset = self:const('relative'):set('signed') - elseif c == 'i' and not args.immediate then - args.immediate = self:const(nil, 'no label') - elseif c == 'I' and not args.index then - args.index = self:const():set('index') - elseif c == 'k' and not args.immediate then - args.immediate = self:const(nil, 'no label'):set('signed'):set('negate') - elseif c == 'K' and not args.immediate then - args.immediate = self:const(nil, 'no label'):set('signed') - elseif c == 'b' and not args.base then - args.base = self:deref():set('tt', 'REG') - else - error('Internal Error: invalid input formatting string') + if c == 'd' then args.rd = self:register(data.registers) + elseif c == 's' then args.rs = self:register(data.registers) + elseif c == 't' then args.rt = self:register(data.registers) + elseif c == 'D' then args.fd = self:register(data.fpu_registers) + elseif c == 'S' then args.fs = self:register(data.fpu_registers) + elseif c == 'T' then args.ft = self:register(data.fpu_registers) + elseif c == 'X' then args.rd = self:register(data.sys_registers) + elseif c == 'Y' then args.rs = self:register(data.sys_registers) + elseif c == 'Z' then args.rt = self:register(data.sys_registers) + elseif c == 'o' then args.offset = self:const():set('signed') + elseif c == 'r' then args.offset = self:const('relative'):set('signed') + elseif c == 'i' then args.immediate = self:const(nil, 'no label') + elseif c == 'I' then args.index = self:const():set('index') + elseif c == 'k' then args.immediate = self:const(nil, 'no label'):set('signed'):set('negate') + elseif c == 'K' then args.immediate = self:const(nil, 'no label'):set('signed') + elseif c == 'b' then args.base = self:deref():set('tt', 'REG') + else error('Internal Error: invalid input formatting string') end end return args @@ -188,7 +171,7 @@ function Dumper:format_out_raw(outformat, first, args, const, formatconst) local out = {} for i=1, #outformat do local c = outformat:sub(i, i) - if c == 'd' then out[#out+1] = args.rd + if c == 'd' then insert(out, args.rd) elseif c == 's' then insert(out, args.rs) elseif c == 't' then insert(out, args.rt) elseif c == 'D' then insert(out, args.fd) diff --git a/Lua/lib/lips/Lexer.lua b/Lua/lib/lips/Lexer.lua index 81ee4e8..91c8fb8 100644 --- a/Lua/lib/lips/Lexer.lua +++ b/Lua/lib/lips/Lexer.lua @@ -300,7 +300,10 @@ function Lexer:lex_include(_yield) if self.options.path then fn = self.options.path..fn end - local sublexer = Lexer(util.readfile(fn), fn, self.options) + + local new_options = setmetatable({}, {__index=self.options}) + new_options.path = fn:match(".*/") + local sublexer = Lexer(util.readfile(fn), fn, new_options) sublexer:lex(_yield) end diff --git a/Lua/lib/lips/Preproc.lua b/Lua/lib/lips/Preproc.lua index ab68da2..c38bf7f 100644 --- a/Lua/lib/lips/Preproc.lua +++ b/Lua/lib/lips/Preproc.lua @@ -146,6 +146,9 @@ function Preproc:process(statements) end insert(new_statements, s) else + for j, t in ipairs(s) do + self:lookup(t) + end insert(new_statements, s) end else @@ -191,7 +194,7 @@ end function Preproc:pop(kind) local ret if kind == nil then - -- noop + ret = self.s[self.i] elseif kind == 'CPU' then ret = self:register(data.registers) elseif kind == 'DEREF' then diff --git a/Lua/lib/lips/data.lua b/Lua/lib/lips/data.lua index 079df18..8df994c 100644 --- a/Lua/lib/lips/data.lua +++ b/Lua/lib/lips/data.lua @@ -2,10 +2,10 @@ local data = {} data.registers = { [0]= - 'R0', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3', - 'T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', - 'S0', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', - 'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA', + 'ZERO', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3', + 'T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', + 'S0', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', + 'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA', } data.sys_registers = { @@ -70,13 +70,11 @@ revtable(data.all_registers) revtable(data.all_directives) -- alternate register names -data.registers['ZERO'] = 0 -data.all_registers['ZERO'] = 0 data.registers['S8'] = 30 data.all_registers['S8'] = 30 for i=0, 31 do - local r = 'REG'..tostring(i) + local r = 'R'..tostring(i) data.registers[r] = i data.all_registers[r] = i end @@ -388,6 +386,10 @@ data.instructions = { BNEZL = {21, 'sr', 's0o'}, -- BNEL RS, R0, offset CL = { 0, 'd', '00d0C', 37}, -- OR RD, R0, R0 MOV = { 0, 'ds', 's0d0C', 37}, -- OR RD, RS, R0 + DMOV = { 0, 'ds', 's0d0C', 45}, -- DADDU RD, RS, R0 +-- bass does it this way +-- MOV = { 0, 'dt', '0td0C', 33}, -- ADDU RD, R0, RT +-- DMOV = { 0, 'dt', '0td0C', 45}, -- DADDU RD, R0, RT NEG = { 0, 'dt', '0td0C', 34}, -- SUB RD, R0, RT NEGU = { 0, 'dt', '0td0C', 35}, -- SUBU RD, R0, RT NOP = { 0, '', '0'}, -- SLL R0, R0, 0 @@ -397,6 +399,11 @@ data.instructions = { SUBI = { 8, 'tsk', 'sti'}, -- ADDI RT, RS, -immediate SUBIU = { 9, 'tsk', 'sti'}, -- ADDIU RT, RS, -immediate + L_D = {53, 'Tob', 'bTo'}, -- LDC1 + L_S = {49, 'Tob', 'bTo'}, -- LWC1 + S_D = {61, 'Tob', 'bTo'}, -- SDC1 + S_S = {57, 'Tob', 'bTo'}, -- SWC1 + -- ...that expand to multiple instructions LI = __, -- only one instruction for values < 0x10000 LA = __, @@ -404,7 +411,9 @@ data.instructions = { -- variable arguments PUSH = __, POP = __, - JPOP = __, + JPOP = __, -- deprecated alias of RET + CALL = __, + RET = __, -- CL = __, overridden to take varargs ABS = o1, -- SRA XOR SUBU @@ -434,12 +443,13 @@ data.instructions = { BLT = o1, BLTU = o1, BGT = o1, BGTU = o1, - BEQI = __, BEQIL = __, - BGEI = __, BGEIL = __, - BGTI = __, BGTIL = __, - BLEI = __, BLEIL = __, - BLTI = __, BLTIL = __, - BNEI = __, BNEIL = __, + -- note: signedness of BEQI/BNEI determines how the immediate is loaded + BEQI = __, BEQIU = __, BEQIL = __, BEQIUL = __, + BGEI = __, BGEIU = __, BGEIL = __, BGEIUL = __, + BGTI = __, BGTIU = __, BGTIL = __, BGTIUL = __, + BLEI = __, BLEIU = __, BLEIL = __, BLEIUL = __, + BLTI = __, BLTIU = __, BLTIL = __, BLTIUL = __, + BNEI = __, BNEIU = __, BNEIL = __, BNEIUL = __, BGEL = o1, BGEUL = o1, BGTL = o1, BGTUL = o1, diff --git a/Lua/lib/lips/init.lua b/Lua/lib/lips/init.lua index f7f6242..13bd35a 100644 --- a/Lua/lib/lips/init.lua +++ b/Lua/lib/lips/init.lua @@ -14,26 +14,7 @@ local path = string.gsub(..., "%.init$", "").."." local util = require(path.."util") local Parser = require(path.."Parser") -function lips.word_writer() - local buff = {} - local max = -1 - return function(pos, b) - if pos then - buff[pos] = ("%02X"):format(b) - if pos > max then - max = pos - end - elseif max >= 0 then - for i=0, max, 4 do - local a = buff[i+0] or '00' - local b = buff[i+1] or '00' - local c = buff[i+2] or '00' - local d = buff[i+3] or '00' - print(a..b..c..d) - end - end - end -end +lips.writers = require(path.."writers") function lips.assemble(fn_or_asm, writer, options) -- assemble MIPS R4300i assembly code. @@ -41,7 +22,7 @@ function lips.assemble(fn_or_asm, writer, options) -- returns error message on error, or nil on success. fn_or_asm = tostring(fn_or_asm) local default_writer = not writer - writer = writer or lips.word_writer() + writer = writer or lips.writers.make_word() options = options or {} local function main() diff --git a/Lua/lib/lips/overrides.lua b/Lua/lib/lips/overrides.lua index 7cdc7a5..d984808 100644 --- a/Lua/lib/lips/overrides.lua +++ b/Lua/lib/lips/overrides.lua @@ -1,8 +1,51 @@ local insert = table.insert +local unpack = rawget(_G, 'unpack') or table.insert local path = string.gsub(..., "[^.]+$", "") local data = require(path.."data") +local function name_pop(name, character) + if name:sub(#name) == character then + return name:sub(1, #name - 1), character + else + return name, '' + end +end + +local function li(self, buffer, dest, im) + if im.tt == 'LABELSYM' then + local im = self:token(im):set('portion', 'upperoff') + insert(buffer, {'LUI', dest, im}) + im = self:token(im):set('portion', 'lower') + insert(buffer, {'ADDIU', dest, dest, im}) + return + end + + if im.portion then + -- FIXME: use appropriate instruction based on portion? + insert(buffer, {'ADDIU', dest, 'R0', im}) + return + end + + im.tok = im.tok % 0x100000000 + if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then + local temp = self:token(im):set('portion', 'upper') + insert(buffer, {'LUI', dest, temp}) + if im.tok % 0x10000 ~= 0 then + local temp = self:token(im):set('portion', 'lower') + insert(buffer, {'ORI', dest, dest, temp}) + end + elseif im.tok >= 0x8000 and im.tok < 0x10000 then + local temp = self:token(im):set('portion', 'lower') + insert(buffer, {'ORI', dest, 'R0', temp}) + else + local temp = self:token(im):set('portion', 'lower') + insert(buffer, {'ADDIU', dest, 'R0', temp}) + end + + return buffer +end + local overrides = {} -- note: "self" is an instance of Preproc @@ -13,6 +56,12 @@ local function tob_override(self, name) if self:peek('DEREF') then offset = 0 base = self:pop('DEREF') + elseif self:peek('REG') then + local o = self:pop('CPU') + local b = self:pop('DEREF'):set('tt', 'REG') + self:push_new('ADDU', 'AT', o, b) + offset = 0 + base = self:token('DEREF', 'AT') else -- NUM or LABELSYM local o = self:pop('CONST') if self:peek('NUM') then @@ -60,26 +109,9 @@ function overrides:LI(name) self:error('use LA for labels') end - if im.portion then - -- FIXME: use appropriate instruction based on portion? - self:push_new('ADDIU', dest, 'R0', im) - return - end - - im.tok = im.tok % 0x100000000 - if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then - local temp = self:token(im):set('portion', 'upper') - self:push_new('LUI', dest, temp) - if im.tok % 0x10000 ~= 0 then - local temp = self:token(im):set('portion', 'lower') - self:push_new('ORI', dest, dest, temp) - end - elseif im.tok >= 0x8000 and im.tok < 0x10000 then - local temp = self:token(im):set('portion', 'lower') - self:push_new('ORI', dest, 'R0', temp) - else - local temp = self:token(im):set('portion', 'lower') - self:push_new('ADDIU', dest, 'R0', temp) + local buffer = li(self, {}, dest, im) + for i, v in ipairs(buffer) do + self:push_new(unpack(v)) end end @@ -87,9 +119,9 @@ function overrides:LA(name) local dest = self:pop('CPU') local im = self:pop('CONST') - local im = self:token(im):set('portion', 'upperoff') + im = self:token(im):set('portion', 'upperoff') self:push_new('LUI', dest, im) - local im = self:token(im):set('portion', 'lower') + im = self:token(im):set('portion', 'lower') self:push_new('ADDIU', dest, dest, im) end @@ -122,16 +154,94 @@ function overrides:PUSH(name) self:push_new(w, r, offset, self:token('DEREF', 'SP')) end end - if name == 'JPOP' then + if name == 'JPOP' or name == 'RET' then self:push_new('JR', 'RA') end - if name == 'POP' or name == 'JPOP' then + if name == 'POP' or name == 'JPOP' or name == 'RET' then local im = #stack * 4 self:push_new('ADDIU', 'SP', 'SP', im) end end overrides.POP = overrides.PUSH overrides.JPOP = overrides.PUSH +overrides.RET = overrides.PUSH + +function overrides:CALL(name) + local func = nil + local stack = {} + for i, t in ipairs(self.s) do + if i == 1 then + func = self:pop() + elseif t.tt == 'REG' then + insert(stack, self:pop('CPU')) + else + insert(stack, self:pop()) + end + end + if func == nil then + self:error(name..' requires at least one argument') + end + + local buffer = {} + -- keep track of An registers used so that + -- we don't use an overwritten value by mistake. + -- in theory, we could set up something to swap + -- An registers around with the minimum use of AT, + -- but that's more complexity than we need for an edge case. + local used = {false, false, false, false} + local need = {false, false, false, false} + + local deref_sp = self:token('DEREF', 'SP') + for i, t in ipairs(stack) do + if t.tt == 'REG' then + if i == 1 and t.tok == 'A0' then -- A0 is already A0, noop. + elseif i == 2 and t.tok == 'A1' then -- etc. + elseif i == 3 and t.tok == 'A2' then + elseif i == 4 and t.tok == 'A3' then + elseif i <= 4 then + if t.tok:sub(1, 1) == 'A' then + local n = tonumber(t.tok:sub(2, 2)) + if used[n + 1] then + self:error("cannot use overwritten register A"..tostring(n), t.tok) + end + end + local dest = 'A'..tostring(i - 1) + insert(buffer, {'MOV', dest, t}) + used[i] = true + else + local offset = (i - 1) * 4 + insert(buffer, {'SW', t, offset, deref_sp}) + end + else + if i <= 4 then + local dest = 'A'..tostring(i - 1) + li(self, buffer, dest, t) + used[i] = true + else + local dest = 'AT' + li(self, buffer, dest, t) + local offset = (i - 1) * 4 + insert(buffer, {'SW', dest, offset, deref_sp}) + end + end + end + + -- if there was just one argument (the function/label), + -- then push a NOP to fill the delay slot. + -- (if the user wants to be efficient, they should be using JAL directly) + if #buffer == 0 then + insert(buffer, {'NOP'}) + end + + -- insert jal as the second to last instruction + -- to place the last instruction in the jal's delay slot. + insert(buffer, #buffer, {'JAL', func}) + + -- finally, write everything out + for i, v in ipairs(buffer) do + self:push_new(unpack(v)) + end +end function overrides:NAND(name) local dest = self:pop('CPU') @@ -157,6 +267,8 @@ function overrides:NORI(name) self:push_new('NOR', dest, dest, 'R0') -- NOT end +-- TODO: ROLV/RORV-like versions of this +-- maybe give the same auto-register treatment to SLL/SRL/SRA too function overrides:ROL(name) local first = name == 'ROL' and 'SLL' or 'SRL' local second = name == 'ROL' and 'SRL' or 'SLL' @@ -199,87 +311,107 @@ function overrides:JR(name) end local branch_basics = { - BEQI = 'BEQ', - BGEI = 'BEQ', - BGTI = 'BEQ', - BLEI = 'BNE', - BLTI = 'BNE', - BNEI = 'BNE', - BEQIL = 'BEQL', - BGEIL = 'BEQL', - BGTIL = 'BEQL', - BLEIL = 'BNEL', - BLTIL = 'BNEL', - BNEIL = 'BNEL', + BEQ = 'BEQ', + BGE = 'BEQ', + BGT = 'BNE', + BLE = 'BEQ', + BLT = 'BNE', + BNE = 'BNE', } -function overrides:BEQI(name) +function overrides:BLT(name) + local likely, unsigned + name, likely = name_pop(name, 'L') + name, unsigned = name_pop(name, 'U') local branch = branch_basics[name] - local reg = self:pop('CPU') - local im = self:pop('CONST') + local a = self:pop('CPU') + local b = self:pop('CPU') local offset = self:pop('CONST') - - if reg == 'AT' then - self:error('register cannot be AT in this pseudo-instruction') - end - - self:push_new('ADDIU', 'AT', 'R0', im) - - self:push_new(branch, reg, 'AT', offset) + self:push_new('SLT'..unsigned, 'AT', a, b) + self:push_new(branch..likely, 'AT', 'R0', offset) +end + +function overrides:BLE(name) + local likely, unsigned + name, likely = name_pop(name, 'L') + name, unsigned = name_pop(name, 'U') + local branch = branch_basics[name] + local a = self:pop('CPU') + local b = self:pop('CPU') + local offset = self:pop('CONST') + self:push_new('SLT'..unsigned, 'AT', b, a) + self:push_new(branch..likely, 'AT', 'R0', offset) end -overrides.BNEI = overrides.BEQI -overrides.BEQIL = overrides.BEQI -overrides.BNEIL = overrides.BEQI function overrides:BLTI(name) - local branch = branch_basics[name] + local likely, unsigned + name, likely = name_pop(name, 'L') + name, unsigned = name_pop(name, 'U') + local branch = branch_basics[name:sub(1, #name - 1)] local reg = self:pop('CPU') local im = self:pop('CONST') local offset = self:pop('CONST') - - if reg == 'AT' then - self:error('register cannot be AT in this pseudo-instruction') - end - - self:push_new('SLTI', 'AT', reg, im) - - self:push_new(branch, 'R0', 'AT', offset) + self:push_new('SLTI'..unsigned, 'AT', reg, im) + self:push_new(branch..likely, 'AT', 'R0', offset) end -overrides.BGEI = overrides.BLTI -overrides.BLTIL = overrides.BLTI -overrides.BGEIL = overrides.BLTI function overrides:BLEI(name) - -- TODO: this can probably be optimized - if name:sub(#name) == 'L' then - self:error('unimplemented pseudo-instruction', name) - end - local branch = branch_basics[name] + local likely, unsigned + name, likely = name_pop(name, 'L') + name, unsigned = name_pop(name, 'U') + local branch = branch_basics[name:sub(1, #name - 1)] + local loadi = unsigned == 'U' and 'ORI' or 'ADDIU' local reg = self:pop('CPU') local im = self:pop('CONST') local offset = self:pop('CONST') - - if reg == 'AT' then - self:error('register cannot be AT in this pseudo-instruction') - end - - self:push_new('ADDIU', 'AT', 'R0', im) - - local beq_offset - if name == 'BLEI' or name =='BLEIL' then - beq_offset = offset - else - -- branch to delay slot of the next branch - beq_offset = self:token('NUM', 2):set('fixed') - end - self:push_new('BEQ', reg, 'AT', beq_offset) - - self:push_new('SLT', 'AT', reg, 'AT') - - self:push_new(branch, 'AT', 'R0', offset) + self:push_new(loadi, 'AT', 'R0', im) + self:push_new('SLT'..unsigned, 'AT', 'AT', reg) + self:push_new(branch..likely, 'AT', 'R0', offset) +end + +function overrides:BEQI(name) + local likely, unsigned + name, likely = name_pop(name, 'L') + name, unsigned = name_pop(name, 'U') + local branch = name:sub(1, #name - 1) + local loadi = unsigned == 'U' and 'ORI' or 'ADDIU' + local reg = self:pop('CPU') + local im = self:pop('CONST') + local offset = self:pop('CONST') + self:push_new(loadi, 'AT', 'R0', im) + self:push_new(branch..likely, reg, 'AT', offset) +end + +local BLT = overrides.BLT +local BLE = overrides.BLE +local BLTI = overrides.BLTI +local BLEI = overrides.BLEI +local BEQI = overrides.BEQI +for k, v in pairs{ + BGE = BLT, BGEI = BLTI, + BGT = BLE, BGTI = BLEI, + + BGEL = BLT, BGEIL = BLTI, + BGTL = BLE, BGTIL = BLEI, + BLEL = BLE, BLEIL = BLEI, + BLTL = BLT, BLTIL = BLTI, + + BGEU = BLT, BGEIU = BLTI, + BGTU = BLE, BGTIU = BLEI, + BLEU = BLE, BLEIU = BLEI, + BLTU = BLT, BLTIU = BLTI, + + BGEUL = BLT, BGEIUL = BLTI, + BGTUL = BLE, BGTIUL = BLEI, + BLEUL = BLE, BLEIUL = BLEI, + BLTUL = BLT, BLTIUL = BLTI, + + BEQI = BEQI, BEQIU = BEQI, + BEQIL = BEQI, BEQIUL = BEQI, + BNEI = BEQI, BNEIU = BEQI, + BNEIL = BEQI, BNEIUL = BEQI, +} do + overrides[k] = v end -overrides.BGTI = overrides.BLEI -overrides.BLEIL = overrides.BLEI -overrides.BGTIL = overrides.BLEI return overrides diff --git a/Lua/lib/lips/writers.lua b/Lua/lib/lips/writers.lua new file mode 100644 index 0000000..45bf6d8 --- /dev/null +++ b/Lua/lib/lips/writers.lua @@ -0,0 +1,85 @@ +local writers = {} + +function writers.make_word() + local buff = {} + local max = -1 + return function(pos, b) + if pos then + buff[pos] = ("%02X"):format(b) + if pos > max then + max = pos + end + elseif max >= 0 then + for i=0, max, 4 do + local a = buff[i+0] or '00' + local b = buff[i+1] or '00' + local c = buff[i+2] or '00' + local d = buff[i+3] or '00' + print(a..b..c..d) + end + end + end +end + +function writers.make_verbose() + local buff = {} + local max = -1 + return function(pos, b) + if pos then + buff[pos] = b + if pos > max then + max = pos + end + elseif max >= 0 then + for i=0, max, 4 do + local a = buff[i+0] or nil + local b = buff[i+1] or nil + local c = buff[i+2] or nil + local d = buff[i+3] or nil + if a or b or c or d then + a = a and ("%02X"):format(a) or '--' + b = b and ("%02X"):format(b) or '--' + c = c and ("%02X"):format(c) or '--' + d = d and ("%02X"):format(d) or '--' + print(('%08X %s'):format(i, a..b..c..d)) + end + end + end + end +end + +function writers.make_tester() + local buff = {} + local max = -1 + return function(pos, b) + if pos then + buff[pos] = b + if pos > max then + max = pos + end + elseif max >= 0 then + local s = '' + local last_i = 0 + for i=0, max, 4 do + local a = buff[i+0] or nil + local b = buff[i+1] or nil + local c = buff[i+2] or nil + local d = buff[i+3] or nil + if a or b or c or d then + a = a and ("%02X"):format(a) or '--' + b = b and ("%02X"):format(b) or '--' + c = c and ("%02X"):format(c) or '--' + d = d and ("%02X"):format(d) or '--' + if last_i ~= i - 4 then + s = s..('@%08X\n'):format(i) + end + s = s..a..b..c..d.."\n" + last_i = i + end + end + return s + end + end +end + +return writers diff --git a/patch/common.sh b/patch/common.sh index 69308da..f999913 100644 --- a/patch/common.sh +++ b/patch/common.sh @@ -76,6 +76,7 @@ copy_rom() { cp *.lua build/ cp "$inject"/*.asm build/ cp *.asm build/ +cp *.bin build/ cd build # don't copy entire dir; avoid copying dotfiles (.git) diff --git a/patch/patch.lua b/patch/patch.lua index c35d123..07a053e 100644 --- a/patch/patch.lua +++ b/patch/patch.lua @@ -1,8 +1,6 @@ #!/usr/bin/env luajit -package.path = package.path..";./?/init.lua" - --require "test.strict" -local assemble = require "lips" +local assemble = require "lips.init" local cereal = require "serialize" local argparse = require "argparse" @@ -34,6 +32,8 @@ local function make_verbose_writer() max = pos end elseif max >= 0 then + -- TODO: optimize. iterating 536,870,912 times to reach 0x800000000 + -- isn't the fastest thing, as you can imagine. for i=0, max, 4 do local a = buff[i+0] or nil local b = buff[i+1] or nil @@ -60,7 +60,7 @@ local function inject(args) if args.output then f = io.open(args.output, 'r+b') if not f then - lament("file not found:", args.output) + lament("file not found: ", args.output) return end end