mirror of
https://github.com/notwa/lips
synced 2025-03-09 19:32:49 -07:00
cleanup and documenting; alias S8 register to FP
This commit is contained in:
parent
3590093470
commit
2694035873
2 changed files with 182 additions and 22 deletions
123
README.md
123
README.md
|
@ -3,3 +3,126 @@
|
|||
An assembler for the MIPS R4300i architecture, written in Lua.
|
||||
|
||||
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.
|
||||
|
|
81
lips.lua
81
lips.lua
|
@ -1,11 +1,11 @@
|
|||
-- i've lost control of my life
|
||||
-- lips.lua
|
||||
|
||||
-- instructions: https://github.com/mikeryan/n64dev/tree/master/docs/n64ops
|
||||
-- lexer and parser are somewhat based on http://chunkbake.luaforge.net/
|
||||
|
||||
local assembler = {
|
||||
_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 = [[
|
||||
Copyright (C) 2015 Connor Olding
|
||||
|
||||
|
@ -35,7 +35,7 @@ local registers = {
|
|||
'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', 'S8', 'RA',
|
||||
'T8', 'T9', 'K0', 'K1', 'GP', 'SP', 'FP', 'RA',
|
||||
}
|
||||
|
||||
local fpu_registers = {
|
||||
|
@ -78,6 +78,8 @@ revtable(all_directives)
|
|||
|
||||
registers['ZERO'] = 0
|
||||
all_registers['ZERO'] = 0
|
||||
registers['S8'] = 30
|
||||
all_registers['S8'] = 30
|
||||
|
||||
for i=0, 31 do
|
||||
local r = 'REG'..tostring(i)
|
||||
|
@ -90,7 +92,50 @@ local fmt_double = 17
|
|||
local fmt_word = 20
|
||||
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'},
|
||||
JAL = {3, 'I', 'I'},
|
||||
|
||||
|
@ -144,12 +189,6 @@ local instruction_handlers = {
|
|||
SLTIU = {11, '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},
|
||||
ADDU = {0, 'dst', 'std0C', 33},
|
||||
AND = {0, 'dst', 'std0C', 36},
|
||||
|
@ -392,7 +431,7 @@ local instruction_handlers = {
|
|||
|
||||
local all_instructions = {}
|
||||
local i = 1
|
||||
for k, v in pairs(instruction_handlers) do
|
||||
for k, v in pairs(instructions) do
|
||||
all_instructions[k:gsub('_', '.')] = i
|
||||
i = i + 1
|
||||
end
|
||||
|
@ -867,15 +906,15 @@ end
|
|||
|
||||
function Parser:instruction()
|
||||
local name = self.tok
|
||||
local h = instruction_handlers[name]
|
||||
local h = instructions[name]
|
||||
self:advance()
|
||||
|
||||
if h == nil then
|
||||
self:error('undefined instruction')
|
||||
elseif h == 'LI' or h == 'LA' then
|
||||
local lui = instruction_handlers['LUI']
|
||||
local addi = instruction_handlers['ADDI']
|
||||
local ori = instruction_handlers['ORI']
|
||||
local lui = instructions['LUI']
|
||||
local addi = instructions['ADDI']
|
||||
local ori = instructions['ORI']
|
||||
local args = {}
|
||||
args.rt = self:register()
|
||||
self:optional_comma()
|
||||
|
@ -899,7 +938,7 @@ function Parser:instruction()
|
|||
self:format_out(addi[3], addi[1], args, addi[4], addi[5])
|
||||
end
|
||||
elseif h[2] == 'tob' then -- or h[2] == 'Tob' then
|
||||
local lui = instruction_handlers['LUI']
|
||||
local lui = instructions['LUI']
|
||||
local args = {}
|
||||
args.rt = self:register()
|
||||
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}
|
||||
end
|
||||
|
||||
function Dumper:define(name, number)
|
||||
end
|
||||
|
||||
function Dumper:add_label(name)
|
||||
self.labels[name] = self.pos
|
||||
end
|
||||
|
@ -1260,9 +1296,9 @@ function Dumper:dump()
|
|||
end
|
||||
|
||||
local function assemble(fn_or_asm, writer)
|
||||
-- assemble MIPS R4300i assembly code
|
||||
-- if fn_or_asm contains a newline; treat as assembly, otherwise load file
|
||||
-- returns error message on error, or nil on success
|
||||
-- assemble MIPS R4300i assembly code.
|
||||
-- if fn_or_asm contains a newline; treat as assembly, otherwise load file.
|
||||
-- returns error message on error, or nil on success.
|
||||
fn_or_asm = tostring(fn_or_asm)
|
||||
writer = writer or io.write
|
||||
|
||||
|
@ -1301,6 +1337,7 @@ return setmetatable(assembler, {
|
|||
registers = registers,
|
||||
fpu_registers = fpu_registers,
|
||||
all_registers = all_registers,
|
||||
instructions = instructions,
|
||||
all_instructions = all_instructions,
|
||||
all_directives = all_directives,
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue