mirror of
https://github.com/notwa/mm
synced 2025-02-05 05:23:22 -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
|
if self:is_EOL() and name == 'ALIGN' then
|
||||||
add(name)
|
add(name)
|
||||||
else
|
else
|
||||||
local size = self:number()
|
local size = self:const(nil, 'no label')
|
||||||
if self:is_EOL() then
|
if self:is_EOL() then
|
||||||
add(name, size)
|
add(name, size)
|
||||||
else
|
else
|
||||||
self:optional_comma()
|
self:optional_comma()
|
||||||
add(name, size, self:number())
|
add(name, size, self:const(nil, 'no label'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then
|
elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then
|
||||||
|
|
|
@ -139,40 +139,23 @@ function Dumper:format_in(informat)
|
||||||
for i=1, #informat do
|
for i=1, #informat do
|
||||||
self.i = i
|
self.i = i
|
||||||
local c = informat:sub(i, i)
|
local c = informat:sub(i, i)
|
||||||
if c == 'd' and not args.rd then
|
if c == 'd' then args.rd = self:register(data.registers)
|
||||||
args.rd = self:register(data.registers)
|
elseif c == 's' then args.rs = self:register(data.registers)
|
||||||
elseif c == 's' and not args.rs then
|
elseif c == 't' then args.rt = self:register(data.registers)
|
||||||
args.rs = self:register(data.registers)
|
elseif c == 'D' then args.fd = self:register(data.fpu_registers)
|
||||||
elseif c == 't' and not args.rt then
|
elseif c == 'S' then args.fs = self:register(data.fpu_registers)
|
||||||
args.rt = self:register(data.registers)
|
elseif c == 'T' then args.ft = self:register(data.fpu_registers)
|
||||||
elseif c == 'D' and not args.fd then
|
elseif c == 'X' then args.rd = self:register(data.sys_registers)
|
||||||
args.fd = self:register(data.fpu_registers)
|
elseif c == 'Y' then args.rs = self:register(data.sys_registers)
|
||||||
elseif c == 'S' and not args.fs then
|
elseif c == 'Z' then args.rt = self:register(data.sys_registers)
|
||||||
args.fs = self:register(data.fpu_registers)
|
elseif c == 'o' then args.offset = self:const():set('signed')
|
||||||
elseif c == 'T' and not args.ft then
|
elseif c == 'r' then args.offset = self:const('relative'):set('signed')
|
||||||
args.ft = self:register(data.fpu_registers)
|
elseif c == 'i' then args.immediate = self:const(nil, 'no label')
|
||||||
elseif c == 'X' and not args.rd then
|
elseif c == 'I' then args.index = self:const():set('index')
|
||||||
args.rd = self:register(data.sys_registers)
|
elseif c == 'k' then args.immediate = self:const(nil, 'no label'):set('signed'):set('negate')
|
||||||
elseif c == 'Y' and not args.rs then
|
elseif c == 'K' then args.immediate = self:const(nil, 'no label'):set('signed')
|
||||||
args.rs = self:register(data.sys_registers)
|
elseif c == 'b' then args.base = self:deref():set('tt', 'REG')
|
||||||
elseif c == 'Z' and not args.rt then
|
else error('Internal Error: invalid input formatting string')
|
||||||
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')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return args
|
return args
|
||||||
|
@ -188,7 +171,7 @@ function Dumper:format_out_raw(outformat, first, args, const, formatconst)
|
||||||
local out = {}
|
local out = {}
|
||||||
for i=1, #outformat do
|
for i=1, #outformat do
|
||||||
local c = outformat:sub(i, i)
|
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 == 's' then insert(out, args.rs)
|
||||||
elseif c == 't' then insert(out, args.rt)
|
elseif c == 't' then insert(out, args.rt)
|
||||||
elseif c == 'D' then insert(out, args.fd)
|
elseif c == 'D' then insert(out, args.fd)
|
||||||
|
|
|
@ -300,7 +300,10 @@ function Lexer:lex_include(_yield)
|
||||||
if self.options.path then
|
if self.options.path then
|
||||||
fn = self.options.path..fn
|
fn = self.options.path..fn
|
||||||
end
|
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)
|
sublexer:lex(_yield)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,9 @@ function Preproc:process(statements)
|
||||||
end
|
end
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
else
|
else
|
||||||
|
for j, t in ipairs(s) do
|
||||||
|
self:lookup(t)
|
||||||
|
end
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -191,7 +194,7 @@ end
|
||||||
function Preproc:pop(kind)
|
function Preproc:pop(kind)
|
||||||
local ret
|
local ret
|
||||||
if kind == nil then
|
if kind == nil then
|
||||||
-- noop
|
ret = self.s[self.i]
|
||||||
elseif kind == 'CPU' then
|
elseif kind == 'CPU' then
|
||||||
ret = self:register(data.registers)
|
ret = self:register(data.registers)
|
||||||
elseif kind == 'DEREF' then
|
elseif kind == 'DEREF' then
|
||||||
|
|
|
@ -2,10 +2,10 @@ local data = {}
|
||||||
|
|
||||||
data.registers = {
|
data.registers = {
|
||||||
[0]=
|
[0]=
|
||||||
'R0', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3',
|
'ZERO', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3',
|
||||||
'T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7',
|
'T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7',
|
||||||
'S0', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7',
|
'S0', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7',
|
||||||
'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA',
|
'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA',
|
||||||
}
|
}
|
||||||
|
|
||||||
data.sys_registers = {
|
data.sys_registers = {
|
||||||
|
@ -70,13 +70,11 @@ revtable(data.all_registers)
|
||||||
revtable(data.all_directives)
|
revtable(data.all_directives)
|
||||||
|
|
||||||
-- alternate register names
|
-- alternate register names
|
||||||
data.registers['ZERO'] = 0
|
|
||||||
data.all_registers['ZERO'] = 0
|
|
||||||
data.registers['S8'] = 30
|
data.registers['S8'] = 30
|
||||||
data.all_registers['S8'] = 30
|
data.all_registers['S8'] = 30
|
||||||
|
|
||||||
for i=0, 31 do
|
for i=0, 31 do
|
||||||
local r = 'REG'..tostring(i)
|
local r = 'R'..tostring(i)
|
||||||
data.registers[r] = i
|
data.registers[r] = i
|
||||||
data.all_registers[r] = i
|
data.all_registers[r] = i
|
||||||
end
|
end
|
||||||
|
@ -388,6 +386,10 @@ data.instructions = {
|
||||||
BNEZL = {21, 'sr', 's0o'}, -- BNEL RS, R0, offset
|
BNEZL = {21, 'sr', 's0o'}, -- BNEL RS, R0, offset
|
||||||
CL = { 0, 'd', '00d0C', 37}, -- OR RD, R0, R0
|
CL = { 0, 'd', '00d0C', 37}, -- OR RD, R0, R0
|
||||||
MOV = { 0, 'ds', 's0d0C', 37}, -- OR RD, RS, 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
|
NEG = { 0, 'dt', '0td0C', 34}, -- SUB RD, R0, RT
|
||||||
NEGU = { 0, 'dt', '0td0C', 35}, -- SUBU RD, R0, RT
|
NEGU = { 0, 'dt', '0td0C', 35}, -- SUBU RD, R0, RT
|
||||||
NOP = { 0, '', '0'}, -- SLL R0, R0, 0
|
NOP = { 0, '', '0'}, -- SLL R0, R0, 0
|
||||||
|
@ -397,6 +399,11 @@ data.instructions = {
|
||||||
SUBI = { 8, 'tsk', 'sti'}, -- ADDI RT, RS, -immediate
|
SUBI = { 8, 'tsk', 'sti'}, -- ADDI RT, RS, -immediate
|
||||||
SUBIU = { 9, 'tsk', 'sti'}, -- ADDIU 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
|
-- ...that expand to multiple instructions
|
||||||
LI = __, -- only one instruction for values < 0x10000
|
LI = __, -- only one instruction for values < 0x10000
|
||||||
LA = __,
|
LA = __,
|
||||||
|
@ -404,7 +411,9 @@ data.instructions = {
|
||||||
-- variable arguments
|
-- variable arguments
|
||||||
PUSH = __,
|
PUSH = __,
|
||||||
POP = __,
|
POP = __,
|
||||||
JPOP = __,
|
JPOP = __, -- deprecated alias of RET
|
||||||
|
CALL = __,
|
||||||
|
RET = __,
|
||||||
-- CL = __, overridden to take varargs
|
-- CL = __, overridden to take varargs
|
||||||
|
|
||||||
ABS = o1, -- SRA XOR SUBU
|
ABS = o1, -- SRA XOR SUBU
|
||||||
|
@ -434,12 +443,13 @@ data.instructions = {
|
||||||
BLT = o1, BLTU = o1,
|
BLT = o1, BLTU = o1,
|
||||||
BGT = o1, BGTU = o1,
|
BGT = o1, BGTU = o1,
|
||||||
|
|
||||||
BEQI = __, BEQIL = __,
|
-- note: signedness of BEQI/BNEI determines how the immediate is loaded
|
||||||
BGEI = __, BGEIL = __,
|
BEQI = __, BEQIU = __, BEQIL = __, BEQIUL = __,
|
||||||
BGTI = __, BGTIL = __,
|
BGEI = __, BGEIU = __, BGEIL = __, BGEIUL = __,
|
||||||
BLEI = __, BLEIL = __,
|
BGTI = __, BGTIU = __, BGTIL = __, BGTIUL = __,
|
||||||
BLTI = __, BLTIL = __,
|
BLEI = __, BLEIU = __, BLEIL = __, BLEIUL = __,
|
||||||
BNEI = __, BNEIL = __,
|
BLTI = __, BLTIU = __, BLTIL = __, BLTIUL = __,
|
||||||
|
BNEI = __, BNEIU = __, BNEIL = __, BNEIUL = __,
|
||||||
|
|
||||||
BGEL = o1, BGEUL = o1,
|
BGEL = o1, BGEUL = o1,
|
||||||
BGTL = o1, BGTUL = o1,
|
BGTL = o1, BGTUL = o1,
|
||||||
|
|
|
@ -14,26 +14,7 @@ local path = string.gsub(..., "%.init$", "").."."
|
||||||
local util = require(path.."util")
|
local util = require(path.."util")
|
||||||
local Parser = require(path.."Parser")
|
local Parser = require(path.."Parser")
|
||||||
|
|
||||||
function lips.word_writer()
|
lips.writers = require(path.."writers")
|
||||||
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 lips.assemble(fn_or_asm, writer, options)
|
function lips.assemble(fn_or_asm, writer, options)
|
||||||
-- assemble MIPS R4300i assembly code.
|
-- 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.
|
-- returns error message on error, or nil on success.
|
||||||
fn_or_asm = tostring(fn_or_asm)
|
fn_or_asm = tostring(fn_or_asm)
|
||||||
local default_writer = not writer
|
local default_writer = not writer
|
||||||
writer = writer or lips.word_writer()
|
writer = writer or lips.writers.make_word()
|
||||||
options = options or {}
|
options = options or {}
|
||||||
|
|
||||||
local function main()
|
local function main()
|
||||||
|
|
|
@ -1,8 +1,51 @@
|
||||||
local insert = table.insert
|
local insert = table.insert
|
||||||
|
local unpack = rawget(_G, 'unpack') or table.insert
|
||||||
|
|
||||||
local path = string.gsub(..., "[^.]+$", "")
|
local path = string.gsub(..., "[^.]+$", "")
|
||||||
local data = require(path.."data")
|
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 = {}
|
local overrides = {}
|
||||||
-- note: "self" is an instance of Preproc
|
-- note: "self" is an instance of Preproc
|
||||||
|
|
||||||
|
@ -13,6 +56,12 @@ local function tob_override(self, name)
|
||||||
if self:peek('DEREF') then
|
if self:peek('DEREF') then
|
||||||
offset = 0
|
offset = 0
|
||||||
base = self:pop('DEREF')
|
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
|
else -- NUM or LABELSYM
|
||||||
local o = self:pop('CONST')
|
local o = self:pop('CONST')
|
||||||
if self:peek('NUM') then
|
if self:peek('NUM') then
|
||||||
|
@ -60,26 +109,9 @@ function overrides:LI(name)
|
||||||
self:error('use LA for labels')
|
self:error('use LA for labels')
|
||||||
end
|
end
|
||||||
|
|
||||||
if im.portion then
|
local buffer = li(self, {}, dest, im)
|
||||||
-- FIXME: use appropriate instruction based on portion?
|
for i, v in ipairs(buffer) do
|
||||||
self:push_new('ADDIU', dest, 'R0', im)
|
self:push_new(unpack(v))
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,9 +119,9 @@ function overrides:LA(name)
|
||||||
local dest = self:pop('CPU')
|
local dest = self:pop('CPU')
|
||||||
local im = self:pop('CONST')
|
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)
|
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)
|
self:push_new('ADDIU', dest, dest, im)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -122,16 +154,94 @@ function overrides:PUSH(name)
|
||||||
self:push_new(w, r, offset, self:token('DEREF', 'SP'))
|
self:push_new(w, r, offset, self:token('DEREF', 'SP'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if name == 'JPOP' then
|
if name == 'JPOP' or name == 'RET' then
|
||||||
self:push_new('JR', 'RA')
|
self:push_new('JR', 'RA')
|
||||||
end
|
end
|
||||||
if name == 'POP' or name == 'JPOP' then
|
if name == 'POP' or name == 'JPOP' or name == 'RET' then
|
||||||
local im = #stack * 4
|
local im = #stack * 4
|
||||||
self:push_new('ADDIU', 'SP', 'SP', im)
|
self:push_new('ADDIU', 'SP', 'SP', im)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
overrides.POP = overrides.PUSH
|
overrides.POP = overrides.PUSH
|
||||||
overrides.JPOP = 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)
|
function overrides:NAND(name)
|
||||||
local dest = self:pop('CPU')
|
local dest = self:pop('CPU')
|
||||||
|
@ -157,6 +267,8 @@ function overrides:NORI(name)
|
||||||
self:push_new('NOR', dest, dest, 'R0') -- NOT
|
self:push_new('NOR', dest, dest, 'R0') -- NOT
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- TODO: ROLV/RORV-like versions of this
|
||||||
|
-- maybe give the same auto-register treatment to SLL/SRL/SRA too
|
||||||
function overrides:ROL(name)
|
function overrides:ROL(name)
|
||||||
local first = name == 'ROL' and 'SLL' or 'SRL'
|
local first = name == 'ROL' and 'SLL' or 'SRL'
|
||||||
local second = name == 'ROL' and 'SRL' or 'SLL'
|
local second = name == 'ROL' and 'SRL' or 'SLL'
|
||||||
|
@ -199,87 +311,107 @@ function overrides:JR(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local branch_basics = {
|
local branch_basics = {
|
||||||
BEQI = 'BEQ',
|
BEQ = 'BEQ',
|
||||||
BGEI = 'BEQ',
|
BGE = 'BEQ',
|
||||||
BGTI = 'BEQ',
|
BGT = 'BNE',
|
||||||
BLEI = 'BNE',
|
BLE = 'BEQ',
|
||||||
BLTI = 'BNE',
|
BLT = 'BNE',
|
||||||
BNEI = 'BNE',
|
BNE = 'BNE',
|
||||||
BEQIL = 'BEQL',
|
|
||||||
BGEIL = 'BEQL',
|
|
||||||
BGTIL = 'BEQL',
|
|
||||||
BLEIL = 'BNEL',
|
|
||||||
BLTIL = 'BNEL',
|
|
||||||
BNEIL = 'BNEL',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 branch = branch_basics[name]
|
||||||
local reg = self:pop('CPU')
|
local a = self:pop('CPU')
|
||||||
local im = self:pop('CONST')
|
local b = self:pop('CPU')
|
||||||
local offset = self:pop('CONST')
|
local offset = self:pop('CONST')
|
||||||
|
self:push_new('SLT'..unsigned, 'AT', a, b)
|
||||||
if reg == 'AT' then
|
self:push_new(branch..likely, 'AT', 'R0', offset)
|
||||||
self:error('register cannot be AT in this pseudo-instruction')
|
end
|
||||||
end
|
|
||||||
|
function overrides:BLE(name)
|
||||||
self:push_new('ADDIU', 'AT', 'R0', im)
|
local likely, unsigned
|
||||||
|
name, likely = name_pop(name, 'L')
|
||||||
self:push_new(branch, reg, 'AT', offset)
|
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
|
end
|
||||||
overrides.BNEI = overrides.BEQI
|
|
||||||
overrides.BEQIL = overrides.BEQI
|
|
||||||
overrides.BNEIL = overrides.BEQI
|
|
||||||
|
|
||||||
function overrides:BLTI(name)
|
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 reg = self:pop('CPU')
|
||||||
local im = self:pop('CONST')
|
local im = self:pop('CONST')
|
||||||
local offset = self:pop('CONST')
|
local offset = self:pop('CONST')
|
||||||
|
self:push_new('SLTI'..unsigned, 'AT', reg, im)
|
||||||
if reg == 'AT' then
|
self:push_new(branch..likely, 'AT', 'R0', offset)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
overrides.BGEI = overrides.BLTI
|
|
||||||
overrides.BLTIL = overrides.BLTI
|
|
||||||
overrides.BGEIL = overrides.BLTI
|
|
||||||
|
|
||||||
function overrides:BLEI(name)
|
function overrides:BLEI(name)
|
||||||
-- TODO: this can probably be optimized
|
local likely, unsigned
|
||||||
if name:sub(#name) == 'L' then
|
name, likely = name_pop(name, 'L')
|
||||||
self:error('unimplemented pseudo-instruction', name)
|
name, unsigned = name_pop(name, 'U')
|
||||||
end
|
local branch = branch_basics[name:sub(1, #name - 1)]
|
||||||
local branch = branch_basics[name]
|
local loadi = unsigned == 'U' and 'ORI' or 'ADDIU'
|
||||||
local reg = self:pop('CPU')
|
local reg = self:pop('CPU')
|
||||||
local im = self:pop('CONST')
|
local im = self:pop('CONST')
|
||||||
local offset = self:pop('CONST')
|
local offset = self:pop('CONST')
|
||||||
|
self:push_new(loadi, 'AT', 'R0', im)
|
||||||
if reg == 'AT' then
|
self:push_new('SLT'..unsigned, 'AT', 'AT', reg)
|
||||||
self:error('register cannot be AT in this pseudo-instruction')
|
self:push_new(branch..likely, 'AT', 'R0', offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:push_new('ADDIU', 'AT', 'R0', im)
|
function overrides:BEQI(name)
|
||||||
|
local likely, unsigned
|
||||||
local beq_offset
|
name, likely = name_pop(name, 'L')
|
||||||
if name == 'BLEI' or name =='BLEIL' then
|
name, unsigned = name_pop(name, 'U')
|
||||||
beq_offset = offset
|
local branch = name:sub(1, #name - 1)
|
||||||
else
|
local loadi = unsigned == 'U' and 'ORI' or 'ADDIU'
|
||||||
-- branch to delay slot of the next branch
|
local reg = self:pop('CPU')
|
||||||
beq_offset = self:token('NUM', 2):set('fixed')
|
local im = self:pop('CONST')
|
||||||
end
|
local offset = self:pop('CONST')
|
||||||
self:push_new('BEQ', reg, 'AT', beq_offset)
|
self:push_new(loadi, 'AT', 'R0', im)
|
||||||
|
self:push_new(branch..likely, reg, 'AT', offset)
|
||||||
self:push_new('SLT', 'AT', reg, 'AT')
|
end
|
||||||
|
|
||||||
self:push_new(branch, 'AT', 'R0', offset)
|
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
|
end
|
||||||
overrides.BGTI = overrides.BLEI
|
|
||||||
overrides.BLEIL = overrides.BLEI
|
|
||||||
overrides.BGTIL = overrides.BLEI
|
|
||||||
|
|
||||||
return overrides
|
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 *.lua build/
|
||||||
cp "$inject"/*.asm build/
|
cp "$inject"/*.asm build/
|
||||||
cp *.asm build/
|
cp *.asm build/
|
||||||
|
cp *.bin build/
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
# don't copy entire dir; avoid copying dotfiles (.git)
|
# don't copy entire dir; avoid copying dotfiles (.git)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#!/usr/bin/env luajit
|
#!/usr/bin/env luajit
|
||||||
package.path = package.path..";./?/init.lua"
|
|
||||||
|
|
||||||
--require "test.strict"
|
--require "test.strict"
|
||||||
local assemble = require "lips"
|
local assemble = require "lips.init"
|
||||||
local cereal = require "serialize"
|
local cereal = require "serialize"
|
||||||
local argparse = require "argparse"
|
local argparse = require "argparse"
|
||||||
|
|
||||||
|
@ -34,6 +32,8 @@ local function make_verbose_writer()
|
||||||
max = pos
|
max = pos
|
||||||
end
|
end
|
||||||
elseif max >= 0 then
|
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
|
for i=0, max, 4 do
|
||||||
local a = buff[i+0] or nil
|
local a = buff[i+0] or nil
|
||||||
local b = buff[i+1] or nil
|
local b = buff[i+1] or nil
|
||||||
|
@ -60,7 +60,7 @@ local function inject(args)
|
||||||
if args.output then
|
if args.output then
|
||||||
f = io.open(args.output, 'r+b')
|
f = io.open(args.output, 'r+b')
|
||||||
if not f then
|
if not f then
|
||||||
lament("file not found:", args.output)
|
lament("file not found: ", args.output)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue