mirror of
https://github.com/notwa/mm
synced 2024-11-05 04:39:03 -08:00
update lips and patch
This commit is contained in:
parent
1f1e7418ba
commit
2edbe2135a
10 changed files with 365 additions and 167 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
85
Lua/lib/lips/writers.lua
Normal file
85
Lua/lib/lips/writers.lua
Normal file
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue