documentation
This commit is contained in:
parent
7b3eaf9b4f
commit
009af92f65
141
asm/creole.py
141
asm/creole.py
|
@ -6,25 +6,52 @@ class MalformedArgument(Exception):
|
|||
pass
|
||||
|
||||
def word_2c(w):
|
||||
""" Negate a non-negative integer using two's compliment. """
|
||||
""" Negate a non-negative integer using 32 bit two's compliment.
|
||||
|
||||
:param w: An integer in two's compliment. A non-negative Python
|
||||
integer will work.
|
||||
:return: The negation of the integer stored as a two's compliment
|
||||
integer.
|
||||
"""
|
||||
return (~w + 1) & 0xFFFFFFFF
|
||||
def ti(w):
|
||||
""" Explicitly transform integer into two's compliment representation. """
|
||||
""" Explicitly transform integer into two's compliment representation.
|
||||
|
||||
:param w: A python integer.
|
||||
:return: The integer in two's compliment.
|
||||
"""
|
||||
return w if w >= 0 else word_wc(-w)
|
||||
def from_2c(w):
|
||||
""" Turn two's compliment word into Python integer. """
|
||||
""" Turn two's compliment word into Python integer.
|
||||
|
||||
:param w: An integer in 32 bit twos compliment.
|
||||
:return: The integer as a proper Python string.
|
||||
"""
|
||||
if (w >> 31) & 1 == 0:
|
||||
return w
|
||||
return -word_2c(w)
|
||||
|
||||
class Argument:
|
||||
""" Class of arguments. Not used directly: It is used to store
|
||||
intermediate information during the assembly process. """
|
||||
|
||||
def __init__(self, argtype, val, sign=False):
|
||||
""" Initialize an argument.
|
||||
|
||||
:param argtype: Type of the argument (instance of ArgType).
|
||||
:param val: Python integer value of the argument.
|
||||
:param sign: If the argument should be treated as signed.
|
||||
Otherwise, the integer will be interpreted in execution
|
||||
as an unsigned integer.
|
||||
"""
|
||||
self.at = argtype
|
||||
self.sign = sign
|
||||
self.val = val
|
||||
def __str__(self):
|
||||
return f'({self.at}, {self.sign}, {self.val})'
|
||||
def high_bits(self):
|
||||
""" Returns the high bits that the argument would have
|
||||
in the opcode. """
|
||||
return int(self.sign) << 1 | (self.at == ArgType.REG)
|
||||
|
||||
class ArgType(Enum):
|
||||
|
@ -47,11 +74,21 @@ class ArgType(Enum):
|
|||
and returns a tuple with the first the first element being
|
||||
the type and the second element being the integer value of
|
||||
the argument.
|
||||
|
||||
Valid parameters are:
|
||||
|
||||
* `r` followed by a nonnegative integer (register)
|
||||
* `l` followed by a nonnegative integer (label)
|
||||
* any integer (immediate value)
|
||||
|
||||
:param s: String representing the argument.
|
||||
:return: The Argument object representing the argument.
|
||||
:raises MalformedArgument:
|
||||
"""
|
||||
if s.isnumeric():
|
||||
return Argument(ArgType.IMM, int(s))
|
||||
elif s[0] == "-" and s[1:].isnumeric():
|
||||
return Argument(ArgType.IMM, word_2c(int(s[1:])))
|
||||
return Argument(ArgType.IMM, word_2c(int(s[1:])), True)
|
||||
elif s[0] == 'r' and s[1:].isnumeric():
|
||||
return Argument(ArgType.REG, int(s[1:]))
|
||||
elif s[0] == 'l' and s[1:].isnumeric():
|
||||
|
@ -61,7 +98,11 @@ class ArgType(Enum):
|
|||
|
||||
def typecheck(self, s):
|
||||
""" Parses the type of the string and returns it if it fits
|
||||
the type of the enum value. """
|
||||
the type of the enum value.
|
||||
|
||||
:param s: String argument representing an argument.
|
||||
:return: The Argument class containing the object, or None
|
||||
if the string does not fit the type of self. """
|
||||
t = ArgType.gettype(s)
|
||||
if self == ArgType.VAL:
|
||||
if t.at == ArgType.REG or t.at == ArgType.IMM:
|
||||
|
@ -76,60 +117,95 @@ class ArgType(Enum):
|
|||
class OpcodeException(Exception):
|
||||
pass
|
||||
class TypecheckLenException(Exception):
|
||||
""" Exception thrown when arguments to an instruction are of the
|
||||
incorrect length. """
|
||||
def __init__(self, opcode, insargs, argtypelen):
|
||||
self.opcode = opcode
|
||||
self.insargs = insargs
|
||||
self.argtypelen = argtypelen
|
||||
def __str__(self):
|
||||
return f'arguments {insargs} to opcode {self.opcode} not length {self.argtypelen}'
|
||||
return f'''\
|
||||
arguments {insargs} to opcode {self.opcode} not of length {self.argtypelen}\
|
||||
'''
|
||||
class TypecheckException(Exception):
|
||||
""" Exception thrown when an argument to an instruction are of the
|
||||
incorrect type. """
|
||||
def __init__(self, argtype, sarg, i, opcode):
|
||||
self.argtype = argtype
|
||||
self.sarg = sarg
|
||||
self.i = i
|
||||
self.opcode = opcode
|
||||
def __str__(self):
|
||||
return f'opcode {self.opcode} has invalid value {self.sarg} (expected {self.argtype} in position {self.i}'
|
||||
return f'''\
|
||||
opcode {self.opcode} has invalid value {self.sarg}
|
||||
(expected {self.argtype} in position {self.i})\
|
||||
'''
|
||||
|
||||
class Instruction(Enum):
|
||||
""" Class of microcode instructions. The first number is the opcode
|
||||
and the suceeding values are the types of each of the
|
||||
arguments. The first argument is the opcode and the second
|
||||
argument is if the argument is a signed variant of another
|
||||
opcode. """
|
||||
NOP = 0, False
|
||||
PUSH = 1, False, ArgType.VAL
|
||||
POP = 2, False, ArgType.REG
|
||||
ADD = 3, False, ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
MUL = 4, False, ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
DIV = 5, False, ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
SDIV = "DIV", True, ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
SYS = 6, False, ArgType.VAL
|
||||
CLB = 7, False, ArgType.LAB
|
||||
JL = 8, False, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLS = "JL", True, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLE = 9, False, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLES = "JLE", True, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JE = 10, False, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JNE = 11, False, ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
argument is what function is used to compile the instruction
|
||||
(some instructions are actually versions of other instructions). """
|
||||
NOP = 0, "_default_render"
|
||||
PUSH = 1, "_default_render", ArgType.VAL
|
||||
POP = 2, "_default_render", ArgType.REG
|
||||
ADD = 3, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
MUL = 4, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
DIV = 5, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
SDIV = "DIV", "_render_change_args", ArgType.REG, ArgType.VAL, ArgType.VAL
|
||||
SYS = 6, "_default_render", ArgType.VAL
|
||||
CLB = 7, "_default_render", ArgType.LAB
|
||||
JL = 8, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLS = "JL", "_render_change_args", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLE = 9, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JLES = "JLE", "_render_change_args", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JE = 10, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
JNE = 11, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
|
||||
|
||||
def __int__(self):
|
||||
return self.opcode
|
||||
""" Returns the opcode associated with the Instruction.
|
||||
If it is a virtual instruction, it will resolve the string
|
||||
name of the opcode and return its opcode. """
|
||||
if type(self.opcode) is int:
|
||||
return self.opcode
|
||||
return int(Instruction[self.opcode])
|
||||
|
||||
def __init__(self, opcode, renderfun, *args):
|
||||
""" Initialize an Instruction. Do not call this function: it is
|
||||
used to make enum values. To add a new instruction, modify
|
||||
the Instruction enum.
|
||||
This function sometimes takes string arguments because
|
||||
certain values may not be loaded until later.
|
||||
|
||||
:param opcode: Opcode of the instruction, or a string
|
||||
containing the case-sensitive name of the instruction from
|
||||
which this instruction derives from.
|
||||
|
||||
:param renderfun: a string with the name of a function
|
||||
in the class that returns the instruction opcode.
|
||||
|
||||
:param *args: Type of each argument to the instruction.
|
||||
The amount of arguments denotes the amount of instructions.
|
||||
"""
|
||||
|
||||
def __init__(self, opcode, signed_instruction, *args):
|
||||
if type(opcode) is int and (opcode > 0x7F or opcode < 0):
|
||||
raise OpcodeException(opcode)
|
||||
|
||||
self.opcode = opcode
|
||||
self.argtypes = args
|
||||
if signed_instruction:
|
||||
self.render = self._render_change_args
|
||||
else:
|
||||
self.render = self._default_render
|
||||
self.render = getattr(self, renderfun)
|
||||
|
||||
def typecheck(self, sargs):
|
||||
""" Pass arguments to the instruction and check if the
|
||||
arguments are correct. """
|
||||
arguments are correct.
|
||||
|
||||
:param sargs: List of arguments to the instruction
|
||||
as strings.
|
||||
:return: List of arguments (as Argument objects).
|
||||
:raises TypeCheckException:
|
||||
:raises TypecheckLenException:
|
||||
"""
|
||||
rargs = []
|
||||
if len(sargs) != len(self.argtypes):
|
||||
raise TypecheckLenException(self.opcode, sargs,
|
||||
|
@ -143,9 +219,6 @@ class Instruction(Enum):
|
|||
rargs.append(t)
|
||||
return rargs
|
||||
|
||||
# The following will be called using OPCODE.render() instead of being
|
||||
# called directly.
|
||||
|
||||
def _render_change_args(self, args):
|
||||
for i in range(0,len(args)):
|
||||
if args[i].at != ArgType.LAB:
|
||||
|
|
5
creole.c
5
creole.c
|
@ -428,6 +428,11 @@ handle_compiletime_immediate(struct creole_env *env,
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO: The compile step can be completely removed in favor of directly
|
||||
* executing the bytecode, disassembling it with creole_parse_line()
|
||||
* at every instance. This will also make the implementation simpler.
|
||||
*/
|
||||
|
||||
enum creole_compiler_ret
|
||||
creole_compile(struct creole_env *env, struct creole_reader *r)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue