1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-05-21 17:43:23 -07:00

cleanup and documenting; alias S8 register to FP

This commit is contained in:
Connor Olding 2015-11-23 14:16:31 -08:00
parent 3590093470
commit 2694035873
2 changed files with 182 additions and 22 deletions

123
README.md
View File

@ -3,3 +3,126 @@
An assembler for the MIPS R4300i architecture, written in Lua. An assembler for the MIPS R4300i architecture, written in Lua.
Not for production. Much of the code is untested and likely to change. Not for production. Much of the code is untested and likely to change.
Even this README is incomplete.
## Syntax
TODO
## Instructions
[refer to these instruction documents.][instrdocs]
[instrdocs]: https://github.com/mikeryan/n64dev/tree/master/docs/n64ops
### Unimplemented
* CACHE
* ERET
* TLBP, TLBR, TLBWI, TLBWR
* BC1F, BC1FL, BC1T, BC1TL
### Unimplemented Pseudo-Instructions
Besides implied arguments for existing instructions, there are:
* ABS, MUL, DIV, REM
* NAND, NANDI, NORI, ROL, ROR
* SEQ, SEQI, SEQIU, SEQU
* SGE, SGEI, SGEIU, SGEU
* SGT, SGTI, SGTIU, SGTU
* SLE, SLEI, SLEIU, SLEU
* SNE, SNEI, SNEIU, SNEU
* BEQI, BNEI, BGE, BGEI, BLE, BLEI, BLT, BLTI, BGT, BGTI
## Registers
In order of numerical value, with intended usage:
* R0: always zero; cannot be written to. 'zero' is an acceptable alias.
* AT: assembler temporary. used by various pseudo-instructions.
user may use freely if they're wary.
* V0, V1: subroutine return values.
* A0 A1 A2 A3: subroutine arguments.
* T0 T1 T2 T3 T4 T5 T6 T7: temporary registers.
* S0 S1 S2 S3 S4 S5 S6 S7: saved registers.
* T8 T9: more temporary registers.
* K0 K1: kernel registers. not recommended to use outside of kernel code.
* GP: global pointer.
* SP: stack pointer.
* FP: frame pointer. 'S8' is an acceptable alias.
* RA: subroutine return address.
* REG#: whereas # is a decimal number from 0 to 31.
aliased to the appropriate register. eg: REG0 is R0, REG1 is at, REG2 is V0.
* f#: coproccesor 1 registers, whereas # is a decimal number from 0 to 31.
### Unimplemented
all coprocessor 0 registers:
```
Index, Random, EntryLo0, EntryLo1,
Context, PageMask, Wired, RESERVED,
BadVAddr, Count, EntryHi, Compare,
Status, Cause, ExceptionPC, PRId,
Config, LLAddr, WatchLo, WatchHi,
XContext, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, CacheErr,
TagLo, TagHi, ErrorEPC, RESERVED
```
## Directives
* BYTE: writes a list of 8-bit numbers until end-of-line.
be wary of potential alignment issues.
* HALFWORD: writes a list of 16-bit numbers until end-of-line.
be wary of potential alignment issues.
* WORD: writes a list of 32-bit numbers until end-of-line.
* SKIP: takes one or two arguments.
* ORG: change the current address for writing to; seeking.
for now, this is untested and likely to cause performance issues.
### Unimplemented
* ALIGN: takes one or two arguments.
unlike some other assemblers,
ALIGN only affects the first immediately following datum.
* FLOAT: writes a list of 32-bit floating point numbers until end-of-line.
this may not get implemented due to a lack of aliasing in vanilla Lua,
and thus accuracy issues.
* ASCII: writes a string using its characters' ASCII values.
* ASCIIZ: same as ASCII, but with a null byte added to the end.
* INC, INCASM, INCLUDE: include an external assembly file as-is at this position.
* INCBIN: write an external binary file as-is at this position.

View File

@ -1,11 +1,11 @@
-- i've lost control of my life -- lips.lua
-- instructions: https://github.com/mikeryan/n64dev/tree/master/docs/n64ops -- instructions: https://github.com/mikeryan/n64dev/tree/master/docs/n64ops
-- lexer and parser are somewhat based on http://chunkbake.luaforge.net/ -- lexer and parser are somewhat based on http://chunkbake.luaforge.net/
local assembler = { local assembler = {
_DESCRIPTION = 'Assembles MIPS assembly files for the R4300i CPU.', _DESCRIPTION = 'Assembles MIPS assembly files for the R4300i CPU.',
_URL = 'https://github.com/notwa/mm/blob/master/Lua/inject/assembler.lua', _URL = 'https://github.com/notwa/lips/',
_LICENSE = [[ _LICENSE = [[
Copyright (C) 2015 Connor Olding Copyright (C) 2015 Connor Olding
@ -35,7 +35,7 @@ local registers = {
'R0', 'AT', 'V0', 'V1', 'A0', 'A1', 'A2', 'A3', 'R0', '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', 'S8', 'RA', 'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA',
} }
local fpu_registers = { local fpu_registers = {
@ -78,6 +78,8 @@ revtable(all_directives)
registers['ZERO'] = 0 registers['ZERO'] = 0
all_registers['ZERO'] = 0 all_registers['ZERO'] = 0
registers['S8'] = 30
all_registers['S8'] = 30
for i=0, 31 do for i=0, 31 do
local r = 'REG'..tostring(i) local r = 'REG'..tostring(i)
@ -90,7 +92,50 @@ local fmt_double = 17
local fmt_word = 20 local fmt_word = 20
local fmt_long = 21 local fmt_long = 21
local instruction_handlers = { local instructions = {
--[[
data guide:
--INSTRUCTION_NAME = {opcode, infmt, outfmt, const, fmtconst},
underscores are translated to dots later.
opcode: the first 6 bits of the instruction.
infmt: the input format; one character per argument.
outfmt: the output format: R-, I-, and J-types are inferred by length.
const: (optional) the number to replace 'C' with in outfmt.
fmtconst: (optional) the number to replace 'F' with in outfmt.
input format guide:
such and such: expects a...
d: register for rd
s: register for rs
t: register for rt
D: floating point register for fd
S: floating point register for fs
T: floating point register for ft
o: constant for offset (uses lower halfword)
b: register to dereference for base
r: relative constant or label (uses lower halfword)
i: immediate (must fit in a halfword)
I: constant or label for index (long jump)
j: immediate (uses lower halfword)
J: immediate (uses upper halfword)
k: immediate to negate (must fit in a halfword)
output format guide:
such and such: writes ... at this position
0: zero (sometimes used to refer to R0)
d: rd
s: rs
t: rt
D: fd
S: fs
T: ft
o: offset
b: base
i: immediate (infmt 'i', 'j', 'J', and 'k' all write to here)
C: constant (given in argument immediately after)
F: format constant (given in argument after constant)
--]]
J = {2, 'I', 'I'}, J = {2, 'I', 'I'},
JAL = {3, 'I', 'I'}, JAL = {3, 'I', 'I'},
@ -144,12 +189,6 @@ local instruction_handlers = {
SLTIU = {11, 'tsj', 'sti'}, SLTIU = {11, 'tsj', 'sti'},
XORI = {14, 'tsj', 'sti'}, XORI = {14, 'tsj', 'sti'},
-- first 6 bits of instruction
-- | input format
-- | | output format
-- | | | const (only if used in output)
-- | | | | format-const (only if used in output)
-- | |____ |______ |_ |_
ADD = {0, 'dst', 'std0C', 32}, ADD = {0, 'dst', 'std0C', 32},
ADDU = {0, 'dst', 'std0C', 33}, ADDU = {0, 'dst', 'std0C', 33},
AND = {0, 'dst', 'std0C', 36}, AND = {0, 'dst', 'std0C', 36},
@ -392,7 +431,7 @@ local instruction_handlers = {
local all_instructions = {} local all_instructions = {}
local i = 1 local i = 1
for k, v in pairs(instruction_handlers) do for k, v in pairs(instructions) do
all_instructions[k:gsub('_', '.')] = i all_instructions[k:gsub('_', '.')] = i
i = i + 1 i = i + 1
end end
@ -867,15 +906,15 @@ end
function Parser:instruction() function Parser:instruction()
local name = self.tok local name = self.tok
local h = instruction_handlers[name] local h = instructions[name]
self:advance() self:advance()
if h == nil then if h == nil then
self:error('undefined instruction') self:error('undefined instruction')
elseif h == 'LI' or h == 'LA' then elseif h == 'LI' or h == 'LA' then
local lui = instruction_handlers['LUI'] local lui = instructions['LUI']
local addi = instruction_handlers['ADDI'] local addi = instructions['ADDI']
local ori = instruction_handlers['ORI'] local ori = instructions['ORI']
local args = {} local args = {}
args.rt = self:register() args.rt = self:register()
self:optional_comma() self:optional_comma()
@ -899,7 +938,7 @@ function Parser:instruction()
self:format_out(addi[3], addi[1], args, addi[4], addi[5]) self:format_out(addi[3], addi[1], args, addi[4], addi[5])
end end
elseif h[2] == 'tob' then -- or h[2] == 'Tob' then elseif h[2] == 'tob' then -- or h[2] == 'Tob' then
local lui = instruction_handlers['LUI'] local lui = instructions['LUI']
local args = {} local args = {}
args.rt = self:register() args.rt = self:register()
self:optional_comma() self:optional_comma()
@ -1042,9 +1081,6 @@ function Dumper:add_instruction_r(o, s, t, d, f, c)
self:push_instruction{o, s, t, d, f, c} self:push_instruction{o, s, t, d, f, c}
end end
function Dumper:define(name, number)
end
function Dumper:add_label(name) function Dumper:add_label(name)
self.labels[name] = self.pos self.labels[name] = self.pos
end end
@ -1260,9 +1296,9 @@ function Dumper:dump()
end end
local function assemble(fn_or_asm, writer) local function assemble(fn_or_asm, writer)
-- assemble MIPS R4300i assembly code -- assemble MIPS R4300i assembly code.
-- if fn_or_asm contains a newline; treat as assembly, otherwise load file -- if fn_or_asm contains a newline; treat as assembly, otherwise load file.
-- 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)
writer = writer or io.write writer = writer or io.write
@ -1301,6 +1337,7 @@ return setmetatable(assembler, {
registers = registers, registers = registers,
fpu_registers = fpu_registers, fpu_registers = fpu_registers,
all_registers = all_registers, all_registers = all_registers,
instructions = instructions,
all_instructions = all_instructions, all_instructions = all_instructions,
all_directives = all_directives, all_directives = all_directives,
}) })