documentation

This commit is contained in:
Peter McGoron 2023-02-12 19:13:37 +00:00
parent 7b3eaf9b4f
commit 009af92f65
2 changed files with 112 additions and 34 deletions

View File

@ -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:

View File

@ -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)
{