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 pass
def word_2c(w): 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 return (~w + 1) & 0xFFFFFFFF
def ti(w): 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) return w if w >= 0 else word_wc(-w)
def from_2c(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: if (w >> 31) & 1 == 0:
return w return w
return -word_2c(w) return -word_2c(w)
class Argument: 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): 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.at = argtype
self.sign = sign self.sign = sign
self.val = val self.val = val
def __str__(self): def __str__(self):
return f'({self.at}, {self.sign}, {self.val})' return f'({self.at}, {self.sign}, {self.val})'
def high_bits(self): 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) return int(self.sign) << 1 | (self.at == ArgType.REG)
class ArgType(Enum): class ArgType(Enum):
@ -47,11 +74,21 @@ class ArgType(Enum):
and returns a tuple with the first the first element being and returns a tuple with the first the first element being
the type and the second element being the integer value of the type and the second element being the integer value of
the argument. 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(): if s.isnumeric():
return Argument(ArgType.IMM, int(s)) return Argument(ArgType.IMM, int(s))
elif s[0] == "-" and s[1:].isnumeric(): 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(): elif s[0] == 'r' and s[1:].isnumeric():
return Argument(ArgType.REG, int(s[1:])) return Argument(ArgType.REG, int(s[1:]))
elif s[0] == 'l' and s[1:].isnumeric(): elif s[0] == 'l' and s[1:].isnumeric():
@ -61,7 +98,11 @@ class ArgType(Enum):
def typecheck(self, s): def typecheck(self, s):
""" Parses the type of the string and returns it if it fits """ 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) t = ArgType.gettype(s)
if self == ArgType.VAL: if self == ArgType.VAL:
if t.at == ArgType.REG or t.at == ArgType.IMM: if t.at == ArgType.REG or t.at == ArgType.IMM:
@ -76,60 +117,95 @@ class ArgType(Enum):
class OpcodeException(Exception): class OpcodeException(Exception):
pass pass
class TypecheckLenException(Exception): class TypecheckLenException(Exception):
""" Exception thrown when arguments to an instruction are of the
incorrect length. """
def __init__(self, opcode, insargs, argtypelen): def __init__(self, opcode, insargs, argtypelen):
self.opcode = opcode self.opcode = opcode
self.insargs = insargs self.insargs = insargs
self.argtypelen = argtypelen self.argtypelen = argtypelen
def __str__(self): 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): class TypecheckException(Exception):
""" Exception thrown when an argument to an instruction are of the
incorrect type. """
def __init__(self, argtype, sarg, i, opcode): def __init__(self, argtype, sarg, i, opcode):
self.argtype = argtype self.argtype = argtype
self.sarg = sarg self.sarg = sarg
self.i = i self.i = i
self.opcode = opcode self.opcode = opcode
def __str__(self): 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 Instruction(Enum):
""" Class of microcode instructions. The first number is the opcode """ Class of microcode instructions. The first number is the opcode
and the suceeding values are the types of each of the and the suceeding values are the types of each of the
arguments. The first argument is the opcode and the second arguments. The first argument is the opcode and the second
argument is if the argument is a signed variant of another argument is what function is used to compile the instruction
opcode. """ (some instructions are actually versions of other instructions). """
NOP = 0, False NOP = 0, "_default_render"
PUSH = 1, False, ArgType.VAL PUSH = 1, "_default_render", ArgType.VAL
POP = 2, False, ArgType.REG POP = 2, "_default_render", ArgType.REG
ADD = 3, False, ArgType.REG, ArgType.VAL, ArgType.VAL ADD = 3, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
MUL = 4, False, ArgType.REG, ArgType.VAL, ArgType.VAL MUL = 4, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
DIV = 5, False, ArgType.REG, ArgType.VAL, ArgType.VAL DIV = 5, "_default_render", ArgType.REG, ArgType.VAL, ArgType.VAL
SDIV = "DIV", True, ArgType.REG, ArgType.VAL, ArgType.VAL SDIV = "DIV", "_render_change_args", ArgType.REG, ArgType.VAL, ArgType.VAL
SYS = 6, False, ArgType.VAL SYS = 6, "_default_render", ArgType.VAL
CLB = 7, False, ArgType.LAB CLB = 7, "_default_render", ArgType.LAB
JL = 8, False, ArgType.LAB, ArgType.VAL, ArgType.VAL JL = 8, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
JLS = "JL", True, ArgType.LAB, ArgType.VAL, ArgType.VAL JLS = "JL", "_render_change_args", ArgType.LAB, ArgType.VAL, ArgType.VAL
JLE = 9, False, ArgType.LAB, ArgType.VAL, ArgType.VAL JLE = 9, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
JLES = "JLE", True, ArgType.LAB, ArgType.VAL, ArgType.VAL JLES = "JLE", "_render_change_args", ArgType.LAB, ArgType.VAL, ArgType.VAL
JE = 10, False, ArgType.LAB, ArgType.VAL, ArgType.VAL JE = 10, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
JNE = 11, False, ArgType.LAB, ArgType.VAL, ArgType.VAL JNE = 11, "_default_render", ArgType.LAB, ArgType.VAL, ArgType.VAL
def __int__(self): 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): if type(opcode) is int and (opcode > 0x7F or opcode < 0):
raise OpcodeException(opcode) raise OpcodeException(opcode)
self.opcode = opcode self.opcode = opcode
self.argtypes = args self.argtypes = args
if signed_instruction: self.render = getattr(self, renderfun)
self.render = self._render_change_args
else:
self.render = self._default_render
def typecheck(self, sargs): def typecheck(self, sargs):
""" Pass arguments to the instruction and check if the """ 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 = [] rargs = []
if len(sargs) != len(self.argtypes): if len(sargs) != len(self.argtypes):
raise TypecheckLenException(self.opcode, sargs, raise TypecheckLenException(self.opcode, sargs,
@ -143,9 +219,6 @@ class Instruction(Enum):
rargs.append(t) rargs.append(t)
return rargs return rargs
# The following will be called using OPCODE.render() instead of being
# called directly.
def _render_change_args(self, args): def _render_change_args(self, args):
for i in range(0,len(args)): for i in range(0,len(args)):
if args[i].at != ArgType.LAB: 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 enum creole_compiler_ret
creole_compile(struct creole_env *env, struct creole_reader *r) creole_compile(struct creole_env *env, struct creole_reader *r)
{ {