# HG changeset patch # User Michael Pavone # Date 1537286802 25200 # Node ID 2d9e8a7b8ba2e0a5cc0015c5574c98001e4d3ce7 # Parent 28ec17387be5dc04644b8479dd3fd1375892b28b Initial commit of CPU DSL and a WIP SVP implementation written in that DSL diff -r 28ec17387be5 -r 2d9e8a7b8ba2 cpu_dsl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpu_dsl.py Tue Sep 18 09:06:42 2018 -0700 @@ -0,0 +1,1086 @@ +#!/usr/bin/env python3 + + +class Block: + def addOp(self, op): + pass + + def processLine(self, parts): + if parts[0] == 'switch': + o = Switch(self, parts[1]) + self.addOp(o) + return o + elif parts[0] == 'if': + o = If(self, parts[1]) + self.addOp(o) + return o + elif parts[0] == 'end': + raise Exception('end is only allowed inside a switch or if block') + else: + self.addOp(NormalOp(parts)) + return self + + def resolveLocal(self, name): + return None + +class ChildBlock(Block): + def processLine(self, parts): + if parts[0] == 'end': + return self.parent + return super().processLine(parts) + +#Represents an instruction of the emulated CPU +class Instruction(Block): + def __init__(self, value, fields, name): + self.value = value + self.fields = fields + self.name = name + self.implementation = [] + self.locals = {} + self.regValues = {} + self.varyingBits = 0 + self.invalidFieldValues = {} + for field in fields: + self.varyingBits += fields[field][1] + + def addOp(self, op): + if op.op == 'local': + name = op.params[0] + size = op.params[1] + self.locals[name] = size + elif op.op == 'invalid': + name = op.params[0] + value = int(op.params[1]) + self.invalidFieldValues.setdefault(name, set()).add(value) + else: + self.implementation.append(op) + + def resolveLocal(self, name): + if name in self.locals: + return name + return None + + def addLocal(self, name, size): + self.locals[name] = size + + def localSize(self, name): + return self.locals.get(name) + + def __lt__(self, other): + if isinstance(other, Instruction): + if self.varyingBits != other.varyingBits: + return self.varyingBits < other.varyingBits + return self.value < other.value + else: + return NotImplemented + + def allValues(self): + values = [] + for i in range(0, 1 << self.varyingBits): + iword = self.value + doIt = True + for field in self.fields: + shift,bits = self.fields[field] + val = i & ((1 << bits) - 1) + if field in self.invalidFieldValues and val in self.invalidFieldValues[field]: + doIt = False + break + i >>= bits + iword |= val << shift + if doIt: + values.append(iword) + return values + + def getFieldVals(self, value): + fieldVals = {} + fieldBits = {} + for field in self.fields: + shift,bits = self.fields[field] + val = (value >> shift) & ((1 << bits) - 1) + fieldVals[field] = val + fieldBits[field] = bits + return (fieldVals, fieldBits) + + def generateName(self, value): + fieldVals,fieldBits = self.getFieldVals(value) + names = list(fieldVals.keys()) + names.sort() + funName = self.name + for name in names: + funName += '_{0}_{1:0>{2}}'.format(name, bin(fieldVals[name])[2:], fieldBits[name]) + return funName + + def generateBody(self, value, prog, otype): + output = [] + prog.meta = {} + prog.currentScope = self + for var in self.locals: + output.append('\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var)) + fieldVals,_ = self.getFieldVals(value) + for op in self.implementation: + op.generate(prog, self, fieldVals, output, otype) + begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{' + if prog.needFlagCoalesce: + begin += prog.flags.coalesceFlags(prog, otype) + if prog.needFlagDisperse: + output.append(prog.flags.disperseFlags(prog, otype)) + return begin + ''.join(output) + '\n}' + + def __str__(self): + pieces = [self.name + ' ' + hex(self.value) + ' ' + str(self.fields)] + for name in self.locals: + pieces.append('\n\tlocal {0} {1}'.format(name, self.locals[name])) + for op in self.implementation: + pieces.append(str(op)) + return ''.join(pieces) + +#Represents the definition of a helper function +class SubRoutine(Block): + def __init__(self, name): + self.name = name + self.implementation = [] + self.args = [] + self.arg_map = {} + self.locals = {} + self.regValues = {} + + def addOp(self, op): + if op.op == 'arg': + name = op.params[0] + size = op.params[1] + self.arg_map[name] = len(self.args) + self.args.append((name, size)) + elif op.op == 'local': + name = op.params[0] + size = op.params[1] + self.locals[name] = size + else: + self.implementation.append(op) + + def resolveLocal(self, name): + if name in self.locals: + return self.name + '_' + name + return None + + def addLocal(self, name, size): + self.locals[name] = size + + def localSize(self, name): + return self.locals.get(name) + + def inline(self, prog, params, output, otype, parent): + if len(params) != len(self.args): + raise Exception('{0} expects {1} arguments, but was called with {2}'.format(self.name, len(self.args), len(params))) + argValues = {} + if parent: + self.regValues = parent.regValues + oldScope = prog.currentScope + prog.currentScope = self + i = 0 + for name,size in self.args: + argValues[name] = params[i] + i += 1 + for name in self.locals: + size = self.locals[name] + output.append('\n\tuint{size}_t {sub}_{local};'.format(size=size, sub=self.name, local=name)) + for op in self.implementation: + op.generate(prog, self, argValues, output, otype) + prog.currentScope = oldScope + + def __str__(self): + pieces = [self.name] + for name,size in self.args: + pieces.append('\n\targ {0} {1}'.format(name, size)) + for name in self.locals: + pieces.append('\n\tlocal {0} {1}'.format(name, self.locals[name])) + for op in self.implementation: + pieces.append(str(op)) + return ''.join(pieces) + +class Op: + def __init__(self, evalFun = None): + self.evalFun = evalFun + self.impls = {} + self.outOp = () + def cBinaryOperator(self, op): + def _impl(prog, params): + if op == '-': + a = params[1] + b = params[0] + else: + a = params[0] + b = params[1] + return '\n\t{dst} = {a} {op} {b};'.format( + dst = params[2], a = a, b = b, op = op + ) + self.impls['c'] = _impl + self.outOp = (2,) + return self + def cUnaryOperator(self, op): + def _impl(prog, params): + return '\n\t{dst} = {op}{a};'.format( + dst = params[1], a = params[0], op = op + ) + self.impls['c'] = _impl + self.outOp = (1,) + return self + def addImplementation(self, lang, outOp, impl): + self.impls[lang] = impl + if not outOp is None: + if type(outOp) is tuple: + self.outOp = outOp + else: + self.outOp = (outOp,) + return self + def evaluate(self, params): + return self.evalFun(*params) + def canEval(self): + return not self.evalFun is None + def numArgs(self): + return self.evalFun.__code__.co_argcount + def generate(self, otype, prog, params, rawParams): + if self.impls[otype].__code__.co_argcount == 2: + return self.impls[otype](prog, params) + else: + return self.impls[otype](prog, params, rawParams) + + +def _xchgCImpl(prog, params, rawParams): + size = prog.paramSize(rawParams[0]) + decl,name = prog.getTemp(size) + return decl + '\n\t{tmp} = {a};\n\t{a} = {b};\n\t{b} = {tmp};'.format(a = params[0], b = params[1], tmp = name) + +def _dispatchCImpl(prog, params): + if len(params) == 1: + table = 'main' + else: + table = params[1] + return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0]) + +def _updateFlagsCImpl(prog, params, rawParams): + i = 0 + last = '' + autoUpdate = set() + explicit = {} + for c in params[0]: + if c.isdigit(): + if last.isalpha(): + num = int(c) + if num > 1: + raise Exception(c + ' is not a valid digit for update_flags') + explicit[last] = num + last = c + else: + raise Exception('Digit must follow flag letter in update_flags') + else: + if last.isalpha(): + autoUpdate.add(last) + last = c + if last.isalpha(): + autoUpdate.add(last) + output = [] + #TODO: handle autoUpdate flags + for flag in autoUpdate: + calc = prog.flags.flagCalc[flag] + calc,_,resultBit = calc.partition('-') + lastDst = prog.resolveReg(prog.lastDst, None, {}) + storage = prog.flags.getStorage(flag) + if calc == 'bit' or calc == 'sign': + if calc == 'sign': + resultBit = prog.paramSize(prog.lastDst) - 1 + else: + resultBit = int(resultBit) + if type(storage) is tuple: + reg,storageBit = storage + reg = prog.resolveReg(reg, None, {}) + if storageBit == resultBit: + #TODO: optimize this case + output.append('\n\t{reg} = ({reg} & ~{mask}) | ({res} & {mask});'.format( + reg = reg, mask = 1 << resultBit, res = lastDst + )) + else: + if resultBit > storageBit: + op = '>>' + shift = resultBit - storageBit + else: + op = '<<' + shift = storageBit - resultBit + output.append('\n\t{reg} = ({reg} & ~{mask}) | ({res} {op} {shift} & {mask});'.format( + reg = reg, mask = 1 << storageBit, res = lastDst, op = op, shift = shift + )) + else: + reg = prog.resolveReg(storage, None, {}) + output.append('\n\t{reg} = {res} & {mask};'.format(reg=reg, res=lastDst, mask = 1 << resultBit)) + elif calc == 'zero': + if type(storage) is tuple: + reg,storageBit = storage + reg = prog.resolveReg(reg, None, {}) + output.append('\n\t{reg} = {res} ? ({reg} & {mask}) : ({reg} | {bit});'.format( + reg = reg, mask = ~(1 << storageBit), res = lastDst, bit = 1 << storageBit + )) + elif prog.paramSize(prog.lastDst) > prog.paramSize(storage): + reg = prog.resolveReg(storage, None, {}) + output.append('\n\t{reg} = {res} != 0;'.format( + reg = reg, res = lastDst + )) + else: + reg = prog.resolveReg(storage, None, {}) + output.append('\n\t{reg} = {res};'.format(reg = reg, res = lastDst)) + elif calc == 'half-carry': + pass + elif calc == 'carry': + pass + elif calc == 'overflow': + pass + elif calc == 'parity': + pass + #TODO: combine explicit flags targeting the same storage location + for flag in explicit: + location = prog.flags.getStorage(flag) + if type(location) is tuple: + reg,bit = location + reg = prog.resolveReg(reg, None, {}) + value = str(1 << bit) + if explicit[flag]: + operator = '|=' + else: + operator = '&=' + value = '~' + value + output.append('\n\t{reg} {op} {val};'.format(reg=reg, op=operator, val=value)) + else: + reg = prog.resolveReg(location, None, {}) + output.append('\n\t{reg} = {val};'.format(reg=reg, val=explicit[flag])) + return ''.join(output) + +def _cmpCImpl(prog, params): + size = prog.paramSize(params[1]) + tmpvar = 'cmp_tmp{sz}__'.format(sz=size) + typename = '' + if not prog.currentScope.resolveLocal(tmpvar): + prog.currentScope.addLocal(tmpvar, size) + typename = 'uint{sz}_t '.format(sz=size) + prog.lastDst = tmpvar + return '\n\t{tp}{var} = {b} - {a};'.format(tp = typename, var = tmpvar, a = params[0], b = params[1]) + +def _asrCImpl(prog, params, rawParams): + shiftSize = prog.paramSize(rawParams[0]) + mask = 1 << (shiftSize - 1) + return '\n\t{dst} = ({a} >> {b}) | ({a} & {mask}'.format(a = params[0], b = params[1], dst = params[2], mask = mask) + +_opMap = { + 'mov': Op(lambda val: val).cUnaryOperator(''), + 'not': Op(lambda val: ~val).cUnaryOperator('~'), + 'lnot': Op(lambda val: 0 if val else 1).cUnaryOperator('!'), + 'neg': Op(lambda val: -val).cUnaryOperator('-'), + 'add': Op(lambda a, b: a + b).cBinaryOperator('+'), + 'sub': Op(lambda a, b: b - a).cBinaryOperator('-'), + 'lsl': Op(lambda a, b: a << b).cBinaryOperator('<<'), + 'lsr': Op(lambda a, b: a >> b).cBinaryOperator('>>'), + 'asr': Op(lambda a, b: a >> b).addImplementation('c', 2, _asrCImpl), + 'and': Op(lambda a, b: a & b).cBinaryOperator('&'), + 'or': Op(lambda a, b: a | b).cBinaryOperator('|'), + 'xor': Op(lambda a, b: a ^ b).cBinaryOperator('^'), + 'cmp': Op().addImplementation('c', None, _cmpCImpl), + 'ocall': Op().addImplementation('c', None, lambda prog, params: '\n\t{pre}{fun}({args});'.format( + pre = prog.prefix, fun = params[0], args = ', '.join(['context'] + [str(p) for p in params[1:]]) + )), + 'cycles': Op().addImplementation('c', None, + lambda prog, params: '\n\tcontext->current_cycle += context->opts->gen.clock_divider * {0};'.format( + params[0] + ) + ), + 'addsize': Op( + lambda a, b: b + (2 * a if a else 1) + ).addImplementation('c', 2, lambda prog, params: '\n\t{dst} = {val} + {sz} ? {sz} * 2 : 1;'.format( + dst = params[1], sz = params[0], val = params[1] + )), + 'decsize': Op( + lambda a, b: b - (2 * a if a else 1) + ).addImplementation('c', 2, lambda prog, params: '\n\t{dst} = {val} - {sz} ? {sz} * 2 : 1;'.format( + dst = params[2], sz = params[0], val = params[1] + )), + 'xchg': Op().addImplementation('c', (0,1), _xchgCImpl), + 'dispatch': Op().addImplementation('c', None, _dispatchCImpl), + 'update_flags': Op().addImplementation('c', None, _updateFlagsCImpl) +} + +#represents a simple DSL instruction +class NormalOp: + def __init__(self, parts): + self.op = parts[0] + self.params = parts[1:] + + def generate(self, prog, parent, fieldVals, output, otype): + procParams = [] + allParamsConst = True + opDef = _opMap.get(self.op) + for param in self.params: + allowConst = (self.op in prog.subroutines or len(procParams) != len(self.params) - 1) and param in parent.regValues + isDst = (not opDef is None) and len(procParams) in opDef.outOp + param = prog.resolveParam(param, parent, fieldVals, allowConst, isDst) + + if (not type(param) is int) and len(procParams) != len(self.params) - 1: + allParamsConst = False + procParams.append(param) + + if self.op == 'meta': + param,_,index = self.params[1].partition('.') + if index: + index = (parent.resolveLocal(index) or index) + if index in fieldVals: + index = str(fieldVals[index]) + param = param + '.' + index + else: + param = parent.resolveLocal(param) or param + if param in fieldVals: + param = fieldVals[index] + prog.meta[self.params[0]] = param + elif self.op == 'dis': + #TODO: Disassembler + pass + elif not opDef is None: + if opDef.canEval() and allParamsConst: + #do constant folding + if opDef.numArgs() >= len(procParams): + raise Exception('Insufficient args for ' + self.op + ' (' + ', '.join(self.params) + ')') + dst = self.params[opDef.numArgs()] + result = opDef.evaluate(procParams[:opDef.numArgs()]) + while dst in prog.meta: + dst = prog.meta[dst] + maybeLocal = parent.resolveLocal(dst) + if maybeLocal: + dst = maybeLocal + parent.regValues[dst] = result + if prog.isReg(dst): + output.append(_opMap['mov'].generate(otype, prog, procParams, self.params)) + else: + output.append(opDef.generate(otype, prog, procParams, self.params)) + elif self.op in prog.subroutines: + prog.subroutines[self.op].inline(prog, procParams, output, otype, parent) + else: + output.append('\n\t' + self.op + '(' + ', '.join([str(p) for p in procParams]) + ');') + prog.lastOp = self + + def __str__(self): + return '\n\t' + self.op + ' ' + ' '.join(self.params) + +#represents a DSL switch construct +class Switch(ChildBlock): + def __init__(self, parent, param): + self.op = 'switch' + self.parent = parent + self.param = param + self.cases = {} + self.regValues = None + self.current_locals = {} + self.case_locals = {} + self.current_case = None + self.default = None + self.default_locals = None + + def addOp(self, op): + if op.op == 'case': + self.cases[int(op.params[0])] = self.current_case = [] + self.case_locals[int(op.params[0])] = self.current_locals = {} + elif op.op == 'default': + self.default = self.current_case = [] + self.default_locals = self.current_locals = {} + elif self.current_case == None: + raise ion('Orphan instruction in switch') + elif op.op == 'local': + name = op.params[0] + size = op.params[1] + self.current_locals[name] = size + else: + self.current_case.append(op) + + def resolveLocal(self, name): + if name in self.current_locals: + return name + return self.parent.resolveLocal(name) + + def addLocal(self, name, size): + self.current_locals[name] = size + + def localSize(self, name): + if name in self.current_locals: + return self.current_locals[name] + return self.parent.localSize(name) + + def generate(self, prog, parent, fieldVals, output, otype): + oldScope = prog.currentScope + prog.currentScope = self + self.regValues = self.parent.regValues + param = prog.resolveParam(self.param, parent, fieldVals) + if type(param) is int: + if param in self.cases: + if self.case_locals[param]: + output.append('\n\t{') + for local in self.case_locals[param]: + output.append('\n\tuint{0}_t {1};'.format(self.case_locals[param][local], local)) + for op in self.cases[param]: + op.generate(prog, self, fieldVals, output, otype) + if self.case_locals[param]: + output.append('\n\t}') + elif self.default: + if self.default_locals: + output.append('\n\t{') + for local in self.default: + output.append('\n\tuint{0}_t {1};'.format(self.default[local], local)) + for op in self.default: + op.generate(prog, self, fieldVals, output, otype) + else: + output.append('\n\tswitch(' + param + ')') + output.append('\n\t{') + for case in self.cases: + output.append('\n\tcase {0}: '.format(case) + '{') + for local in self.case_locals[case]: + output.append('\n\tuint{0}_t {1};'.format(self.case_locals[case][local], local)) + for op in self.cases[case]: + op.generate(prog, self, fieldVals, output, otype) + output.append('\n\tbreak;') + output.append('\n\t}') + if self.default: + output.append('\n\tdefault: {') + for local in self.default_locals: + output.append('\n\tuint{0}_t {1};'.format(self.default_locals[local], local)) + for op in self.default: + op.generate(prog, self, fieldVals, output, otype) + output.append('\n\t}') + prog.currentScope = oldScope + + def __str__(self): + keys = self.cases.keys() + keys.sort() + lines = ['\n\tswitch'] + for case in keys: + lines.append('\n\tcase {0}'.format(case)) + lines.append(''.join([str(op) for op in self.cases[case]])) + lines.append('\n\tend') + return ''.join(lines) + + +def _geuCImpl(prog, parent, fieldVals, output): + if prog.lastOp.op == 'cmp': + output.pop() + params = prog.lastOp.params + for i in range(0, len(params)): + params[i] = prog.resolveParam(params[i], parent, fieldVals) + return '\n\tif ({a} >= {b}) '.format(a=params[0], b = params[1]) + '{' + else: + raise ion(">=U not implemented in the general case yet") + +_ifCmpImpl = { + 'c': { + '>=U': _geuCImpl + } +} +#represents a DSL conditional construct +class If(ChildBlock): + def __init__(self, parent, cond): + self.op = 'if' + self.parent = parent + self.cond = cond + self.body = [] + self.elseBody = [] + self.curBody = self.body + self.locals = {} + self.regValues = parent.regValues + + def addOp(self, op): + if op.op in ('case', 'arg'): + raise Exception(self.op + ' is not allows inside an if block') + if op.op == 'local': + name = op.params[0] + size = op.params[1] + self.locals[name] = size + elif op.op == 'else': + self.curBody = self.elseBody + else: + self.curBody.append(op) + + def generate(self, prog, parent, fieldVals, output, otype): + try: + if prog.checkBool(self.cond): + for op in self.body: + op.generate(prog, self, fieldVals, output, otype) + else: + for op in self.elseBody: + op.generate(prog, self, fieldVals, output, otype) + except Exception: + if self.cond in _ifCmpImpl[otype]: + output.append(_ifCmpImpl[otype][self.cond](prog, parent, fieldVals, output)) + for op in self.body: + op.generate(prog, self, fieldVals, output, otype) + if self.elseBody: + output.append('\n\t} else {') + for op in self.elseBody: + op.generate(prog, self, fieldVals, output, otype) + output.append('\n\t}') + else: + cond = prog.resolveParam(self.cond, parent, fieldVals) + if type(cond) is int: + if cond: + for op in self.body: + op.generate(prog, self, fieldVals, output, otype) + else: + for op in self.elseBody: + op.generate(prog, self, fieldVals, output, otype) + else: + output.append('\n\tif ({cond}) {'.format(cond=cond)) + for op in self.body: + op.generate(prog, self, fieldVals, output, otype) + if self.elseBody: + output.append('\n\t} else {') + for op in self.elseBody: + op.generate(prog, self, fieldVals, output, otype) + output.append('\n\t}') + + + def __str__(self): + lines = ['\n\tif'] + for op in self.body: + lines.append(str(op)) + lines.append('\n\tend') + return ''.join(lines) + +class Registers: + def __init__(self): + self.regs = {} + self.regArrays = {} + self.regToArray = {} + + def addReg(self, name, size): + self.regs[name] = size + + def addRegArray(self, name, size, regs): + self.regArrays[name] = (size, regs) + idx = 0 + for reg in regs: + self.regs[reg] = size + self.regToArray[reg] = (name, idx) + idx += 1 + + def isReg(self, name): + return name in self.regs + + def isRegArray(self, name): + return name in self.regArrays + + def isRegArrayMember(self, name): + return name in self.regToArray + + def arrayMemberParent(self, name): + return self.regToArray[name][0] + + def arrayMemberIndex(self, name): + return self.regToArray[name][1] + + def arrayMemberName(self, array, index): + if type(index) is int: + return self.regArrays[array][1][index] + else: + return None + + def processLine(self, parts): + if len(parts) > 2: + self.addRegArray(parts[0], int(parts[1]), parts[2:]) + else: + self.addReg(parts[0], int(parts[1])) + return self + +class Flags: + def __init__(self): + self.flagBits = {} + self.flagCalc = {} + self.flagStorage = {} + self.flagReg = None + self.maxBit = -1 + + def processLine(self, parts): + if parts[0] == 'register': + self.flagReg = parts[1] + else: + flag,bit,calc,storage = parts + bit,_,top = bit.partition('-') + bit = int(bit) + if top: + top = int(bit) + if top > self.maxBit: + self.maxBit = top + self.flagBits[flag] = (bit,top) + else: + if bit > self.maxBit: + self.maxBit = bit + self.flagBits[flag] = bit + self.flagCalc[flag] = calc + self.flagStorage[flag] = storage + return self + + def getStorage(self, flag): + if not flag in self.flagStorage: + raise Exception('Undefined flag ' + flag) + loc,_,bit = self.flagStorage[flag].partition('.') + if bit: + return (loc, int(bit)) + else: + return loc + + def disperseFlags(self, prog, otype): + bitToFlag = [None] * (self.maxBit+1) + src = prog.resolveReg(self.flagReg, None, {}) + output = [] + for flag in self.flagBits: + bit = self.flagBits[flag] + if type(bit) is tuple: + bot,top = bit + mask = ((1 << (top + 1 - bot)) - 1) << bot + output.append('\n\t{dst} = {src} & mask;'.format( + dst=prog.resolveReg(self.flagStorage[flag], None, {}), src=src, mask=mask + )) + else: + bitToFlag[self.flagBits[flag]] = flag + multi = {} + for bit in range(len(bitToFlag)-1,-1,-1): + flag = bitToFlag[bit] + if not flag is None: + field,_,dstbit = self.flagStorage[flag].partition('.') + dst = prog.resolveReg(field, None, {}) + if dstbit: + dstbit = int(dstbit) + multi.setdefault(dst, []).append((dstbit, bit)) + else: + output.append('\n\t{dst} = {src} & {mask};'.format(dst=dst, src=src, mask=(1 << bit))) + for dst in multi: + didClear = False + direct = [] + for dstbit, bit in multi[dst]: + if dstbit == bit: + direct.append(bit) + else: + if not didClear: + output.append('\n\t{dst} = 0;'.format(dst=dst)) + didClear = True + if dstbit > bit: + shift = '<<' + diff = dstbit - bit + else: + shift = '>>' + diff = bit - dstbit + output.append('\n\t{dst} |= {src} {shift} {diff} & {mask};'.format( + src=src, dst=dst, shift=shift, diff=diff, mask=(1 << dstbit) + )) + if direct: + if len(direct) == len(multi[dst]): + output.append('\n\t{dst} = {src};'.format(dst=dst, src=src)) + else: + mask = 0 + for bit in direct: + mask = mask | (1 << bit) + output.append('\n\t{dst} = {src} & {mask};'.format(dst=dst, src=src, mask=mask)) + return ''.join(output) + + def coalesceFlags(self, prog, otype): + dst = prog.resolveReg(self.flagReg, None, {}) + output = ['\n\t{dst} = 0;'.format(dst=dst)] + bitToFlag = [None] * (self.maxBit+1) + for flag in self.flagBits: + bit = self.flagBits[flag] + if type(bit) is tuple: + bot,_ = bit + src = prog.resolveReg(self.flagStorage[flag], None, {}) + if bot: + output.append('\n\t{dst} |= {src} << {shift};'.format( + dst=dst, src = src, shift = bot + )) + else: + output.append('\n\t{dst} |= {src};'.format( + dst=dst, src = src + )) + else: + bitToFlag[bit] = flag + multi = {} + for bit in range(len(bitToFlag)-1,-1,-1): + flag = bitToFlag[bit] + if not flag is None: + field,_,srcbit = self.flagStorage[flag].partition('.') + src = prog.resolveReg(field, None, {}) + if srcbit: + srcbit = int(srcbit) + multi.setdefault(src, []).append((srcbit,bit)) + else: + output.append('\n\tif ({src}) {{\n\t\t{dst} |= 1 << {bit};\n\t}}'.format( + dst=dst, src=src, bit=bit + )) + for src in multi: + direct = 0 + for srcbit, dstbit in multi[src]: + if srcbit == dstbit: + direct = direct | (1 << srcbit) + else: + output.append('\n\tif ({src} & (1 << {srcbit})) {{\n\t\t{dst} |= 1 << {dstbit};\n\t}}'.format( + src=src, dst=dst, srcbit=srcbit, dstbit=dstbit + )) + if direct: + output.append('\n\t{dst} |= {src} & {mask}'.format( + dst=dst, src=src, mask=direct + )) + return ''.join(output) + + +class Program: + def __init__(self, regs, instructions, subs, info, flags): + self.regs = regs + self.instructions = instructions + self.subroutines = subs + self.meta = {} + self.booleans = {} + self.prefix = info.get('prefix', [''])[0] + self.opsize = int(info.get('opcode_size', ['8'])[0]) + self.extra_tables = info.get('extra_tables', []) + self.context_type = self.prefix + 'context' + self.body = info.get('body', [None])[0] + self.flags = flags + self.lastDst = None + self.currentScope = None + self.lastOp = None + + def __str__(self): + pieces = [] + for reg in self.regs: + pieces.append(str(self.regs[reg])) + for name in self.subroutines: + pieces.append('\n'+str(self.subroutines[name])) + for instruction in self.instructions: + pieces.append('\n'+str(instruction)) + return ''.join(pieces) + + def build(self, otype): + body = [] + pieces = [] + for table in self.instructions: + opmap = [None] * (1 << self.opsize) + bodymap = {} + instructions = self.instructions[table] + instructions.sort() + for inst in instructions: + for val in inst.allValues(): + if opmap[val] is None: + self.meta = {} + self.temp = {} + self.needFlagCoalesce = False + self.needFlagDisperse = False + self.lastOp = None + opmap[val] = inst.generateName(val) + bodymap[val] = inst.generateBody(val, self, otype) + + pieces.append('\nstatic void *impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) + for inst in range(0, len(opmap)): + op = opmap[inst] + if op is None: + pieces.append('\n\tunimplemented,') + else: + pieces.append('\n\t' + op + ',') + body.append(bodymap[inst]) + pieces.append('\n};') + if self.body in self.subroutines: + pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) + pieces.append('\n{') + pieces.append('\n\twhile (context->current_cycle < target_cycle)') + pieces.append('\n\t{') + self.meta = {} + self.temp = {} + self.subroutines[self.body].inline(self, [], pieces, otype, None) + pieces.append('\n\t}') + pieces.append('\n}') + return ''.join(body) + ''.join(pieces) + + def checkBool(self, name): + if not name in self.booleans: + raise Exception(name + ' is not a defined boolean flag') + return self.booleans[name] + + def getTemp(self, size): + if size in self.temp: + return ('', self.temp[size]) + self.temp[size] = 'tmp{sz}'.format(sz=size); + return ('\n\tuint{sz}_t tmp{sz};'.format(sz=size), self.temp[size]) + + def resolveParam(self, param, parent, fieldVals, allowConstant=True, isdst=False): + keepGoing = True + while keepGoing: + keepGoing = False + try: + if type(param) is int: + pass + elif param.startswith('0x'): + param = int(param, 16) + else: + param = int(param) + except ValueError: + + if parent: + if param in parent.regValues and allowConstant: + return parent.regValues[param] + maybeLocal = parent.resolveLocal(param) + if maybeLocal: + return maybeLocal + if param in fieldVals: + param = fieldVals[param] + elif param in self.meta: + param = self.meta[param] + keepGoing = True + elif self.isReg(param): + param = self.resolveReg(param, parent, fieldVals, isdst) + return param + + def isReg(self, name): + if not type(name) is str: + return False + begin,sep,_ = name.partition('.') + if sep: + if begin in self.meta: + begin = self.meta[begin] + return self.regs.isRegArray(begin) + else: + return self.regs.isReg(name) + + def resolveReg(self, name, parent, fieldVals, isDst=False): + begin,sep,end = name.partition('.') + if sep: + if begin in self.meta: + begin = self.meta[begin] + if not self.regs.isRegArrayMember(end): + end = self.resolveParam(end, parent, fieldVals) + if not type(end) is int and self.regs.isRegArrayMember(end): + arrayName = self.regs.arrayMemberParent(end) + end = self.regs.arrayMemberIndex(end) + if arrayName != begin: + end = 'context->{0}[{1}]'.format(arrayName, end) + regName = self.regs.arrayMemberName(begin, end) + ret = 'context->{0}[{1}]'.format(begin, end) + else: + regName = name + if self.regs.isRegArrayMember(name): + arr,idx = self.regs.regToArray[name] + ret = 'context->{0}[{1}]'.format(arr, idx) + else: + ret = 'context->' + name + if regName == self.flags.flagReg: + if isDst: + self.needFlagDisperse = True + else: + self.needFlagCoalesce = True + if isDst: + self.lastDst = regName + return ret + + + + def paramSize(self, name): + size = self.currentScope.localSize(name) + if size: + return size + begin,sep,_ = name.partition('.') + if sep and self.regs.isRegArray(begin): + return self.regs.regArrays[begin][0] + if self.regs.isReg(name): + return self.regs.regs[name] + return 32 + +def parse(f): + instructions = {} + subroutines = {} + registers = None + flags = None + errors = [] + info = {} + line_num = 0 + cur_object = None + for line in f: + line_num += 1 + line,_,comment = line.partition('#') + if not line.strip(): + continue + if line[0].isspace(): + if not cur_object is None: + parts = [el.strip() for el in line.split(' ')] + if type(cur_object) is dict: + cur_object[parts[0]] = parts[1:] + else: + cur_object = cur_object.processLine(parts) + +# if type(cur_object) is Registers: +# if len(parts) > 2: +# cur_object.addRegArray(parts[0], int(parts[1]), parts[2:]) +# else: +# cur_object.addReg(parts[0], int(parts[1])) +# elif type(cur_object) is dict: +# cur_object[parts[0]] = parts[1:] +# elif parts[0] == 'switch': +# o = Switch(cur_object, parts[1]) +# cur_object.addOp(o) +# cur_object = o +# elif parts[0] == 'if': +# o = If(cur_object, parts[1]) +# cur_object.addOp(o) +# cur_object = o +# elif parts[0] == 'end': +# cur_object = cur_object.parent +# else: +# cur_object.addOp(NormalOp(parts)) + else: + errors.append("Orphan instruction on line {0}".format(line_num)) + else: + parts = line.split(' ') + if len(parts) > 1: + if len(parts) > 2: + table,bitpattern,name = parts + else: + bitpattern,name = parts + table = 'main' + value = 0 + fields = {} + curbit = len(bitpattern) - 1 + for char in bitpattern: + value <<= 1 + if char in ('0', '1'): + value |= int(char) + else: + if char in fields: + fields[char] = (curbit, fields[char][1] + 1) + else: + fields[char] = (curbit, 1) + curbit -= 1 + cur_object = Instruction(value, fields, name.strip()) + instructions.setdefault(table, []).append(cur_object) + elif line.strip() == 'regs': + if registers is None: + registers = Registers() + cur_object = registers + elif line.strip() == 'info': + cur_object = info + elif line.strip() == 'flags': + if flags is None: + flags = Flags() + cur_object = flags + else: + cur_object = SubRoutine(line.strip()) + subroutines[cur_object.name] = cur_object + if errors: + print(errors) + else: + p = Program(registers, instructions, subroutines, info, flags) + p.booleans['dynarec'] = False + p.booleans['interp'] = True + + print('#include "m68k_prefix.c"') + print(p.build('c')) + +def main(argv): + f = open(argv[1]) + parse(f) + +if __name__ == '__main__': + from sys import argv + main(argv) \ No newline at end of file diff -r 28ec17387be5 -r 2d9e8a7b8ba2 svp.cpu --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svp.cpu Tue Sep 18 09:06:42 2018 -0700 @@ -0,0 +1,545 @@ +info + prefix svp_ + opcode_size 16 + body svp_run_op + +regs + internal 16 scratch1 x y scratch2 st pad pc + a 32 + stack 16 stack0 stack1 stack2 stack3 stack4 stack5 + stackidx 8 + p 32 + external 16 pm0 pm1 pm2 xst pm4 ext5 pmc + pointers0 8 r0 r1 r2 r3 + pointers1 8 r4 r5 r6 r7 + iram 16 1024 + ram0 16 256 + ram1 16 256 + zflag 8 + nflag 8 + rpl 16 + +flags + register st + Z 13 zero zflag + N 15 sign nflag + R 0-2 none rpl + +svp_pop + mov stack.stackidx dst + add 1 stackidx stackidx + switch stackidx + case 6 + mov 0 stackidx + end + +svp_push + arg src 16 + add -1 stackidx stackidx + switch stackidx + case -1 + mov 5 stackidx + end + mov src stack.stackidx + +svp_ram_read + arg mode 16 + arg banki 16 + arg regi 16 + local idx 16 + + switch banki + case 0 + meta bank ram0 + meta reg pointers0.regi + + default + meta bank ram1 + meta reg pointers1.regi + end + + mov reg idx + switch mode + case 0 + meta modestr "" + + case 1 + meta modestr +! + add 1 reg reg + + case 2 + #loop decremenet + meta modestr - + mov reg tmp + switch rpl + case 0 + sub 1 reg reg + + default + lsl 1 rpl rpl + sub 1 rpl rpl + local mask 16 + not rpl mask + and reg mask reg + sub 1 tmp tmp + and rpl tmp tmp + or rpl reg reg + + end + + case 3 + #loop increment + meta modestr + + + and 7 st rpl + switch rpl + case 0 + sub 1 reg reg + + default + mov reg tmp + lsl 1 rpl rpl + sub 1 rpl rpl + local mask 16 + not rpl mask + and reg mask reg + add 1 tmp tmp + and rpl tmp tmp + or rpl reg reg + + end + end + + and 255 idx idx + meta val bank.idx + +svp_read_ext + arg regidxr 16 + switch regidxr + case 7 + meta val a + + default + #TODO: PMAR stuff + meta val external.regidxr + end + +svp_write_ext + arg regidxw 16 + switch regidxw + case 7 + and 0xFFFF0000 a a + or src a a + + default + #TODO: PMAR stuff + mov src external.regidxw + end + +svp_alu_op + arg P 16 + arg param 32 + + switch P + case 1 + dis "sub %s" name + sub param a a + + case 3 + dis "cmp %s" name + cmp param a + + case 4 + dis "add %s" name + add param a a + + case 5 + dis "and %s" name + and param a a + + case 6 + dis "or %s" name + or param a a + + case 7 + dis "eor %s" name + xor param a a + end + update_flags ZN + +svp_check_cond + arg fval 16 + arg cond 16 + local invert 8 + switch cond + case 0 + meta flag 1 + + case 5 + meta flag zflag + + case 7 + meta flag nflag + + default + meta flag 0 + end + switch fval + case 0 + lnot flag invert + meta istrue invert + + default + meta istrue flag + end + +PPP0000000000000 alu_n1 + invalid P 0 + invalid P 2 + meta name "-" + svp_alu_op P 0xFFFF0000 + +PPP0000000000RRR alu_r + invalid P 0 + invalid P 2 + local tmp 32 + lsl internal.R 16 tmp + meta name internal.R + svp_alu_op P tmp + +PPP0000000000011 alu_a + invalid P 0 + invalid P 2 + svp_alu_op P a + +PPP0000000000101 alu_stack + invalid P 0 + invalid P 2 + local tmp 32 + meta dst tmp + svp_pop + meta name "st" + svp_alu_op P tmp + +PPP0000000000111 alu_p + invalid P 0 + invalid P 2 + meta name p + svp_alu_op P p + +PPP0000000001RRR alu_ext + invalid P 0 + invalid P 2 + local tmp 32 + svp_read_ext R + lsl val 16 tmp + meta name val + svp_alu_op P tmp + +PPP0001B000MMRR alu_ram + invalid P 0 + invalid P 2 + svp_ram_read M B R + local tmp 32 + lsl val 16 tmp + + switch P + case 1 + dis "sub (%s%s)" reg modestr + sub tmp a a + + case 3 + dis "cmp (%s%s)" reg modestr + cmp tmp a + + case 4 + dis "add (%s%s)" reg modestr + add tmp a a + + case 5 + dis "and (%s%s)" reg modestr + and tmp a a + + case 6 + dis "or (%s%s)" reg modestr + or tmp a a + + case 7 + dis "eor (%s%s)" reg modestr + xor tmp a a + end + + update_flags ZN + +PPP0000000001111 alu_al + invalid P 0 + invalid P 2 + local tmp 32 + lsl a 16 tmp + + meta name al + svp_alu_op P tmp + +1001000FCCCC0OOO cond_mod + svp_check_cond F C + switch istrue + case 0 + + default + switch O + case 2 + asr a 1 a + update_flags ZN + + case 3 + lsl a 1 a + update_flags ZN + + case 6 + neg a a + update_flags ZN + + case 7 + abs a a + update_flags N + end + +000000000DDD0SSS ld_int_int + dis "ld %s, %s" internal.D internal.S + mov internal.S internal.D + +000000000DDD0101 ld_int_st + dis "ld %s, st" internal.D + meta dst internal.D + svp_pop + +0000000000110101 ld_a_st + dis "ld a, st" + local tmp 32 + meta dst tmp + svp_pop + lsl tmp 16 tmp + and 0xFFFF a a + or tmp a a + +0000000001110101 ld_p_st + dis "ld p, st" + local tmp 32 + meta dst tmp + svp_pop + lsl tmp 16 tmp + and 0xFFFF p p + or tmp p p + +0000000001010SSS ld_st_int + dis "ld st, %s" internal.S + svp_push internal.S + +0000000001010011 ld_st_a + dis "ld st, a" + local tmp 32 + lsr a 16 tmp + svp_push tmp + +0000000001010111 ld_st_p + dis "ld st, p" + local tmp 32 + lsr p 16 tmp + svp_push tmp + +0000000000000000 ld_n1_n1 + #nop? + dis "ld -, -" + +0000000000000SSS ld_n1_int + #nop? + dis "nop??" + +0000000000110111 ld_a_p + dis "ld a, p" + mov p a + +0000000001110011 ld_p_a + dis "ld p, a" + mov a p + +0000000000110011 ld_a_a + dis "ld a, a" + mov a a + +0000000001110111 ld_p_p + dis "ld p, p" + mov p p + +000000000DDD0111 ld_int_p + local tmp 32 + lsr p 16 tmp + mov tmp internal.D + dis "ld %s, p" internal.D + +000000000DDD0111 ld_int_a + local tmp 32 + lsr a 16 tmp + mov tmp internal.D + dis "ld %s, a" internal.D + +0000000001110SSS ld_p_int + local tmp 32 + lsl internal.S 16 tmp + mov tmp p + dis "ld p, %s" internal.S + +0000000000110SSS ld_a_int + local tmp 32 + lsl internal.S 16 tmp + mov tmp a + dis "ld a, %s" internal.S + +000000000DDD0000 ld_int_n1 + dis "ld %s, -" internal.D + mov 0xFFFF internal.D + +0000000000110000 ld_a_n1 + dis "ld a, -" + and 0xFFFF a a + or 0xFFFF0000 a a + +0000000001110000 ld_p_n1 + dis "ld p, -" + and 0xFFFF p p + or 0xFFFF0000 p p + +000000000DDD1SSS ld_int_ext + svp_read_ext S + dis "ld %s, %s" internal.D val + mov val internal.D + +0000000000111SSS ld_a_ext + svp_read_ext S + dis "ld a, %s" val + local tmp 32 + lsl val 16 tmp + and 0xFFFF a a + or tmp a a + +0000000001111SSS ld_p_ext + svp_read_ext S + dis "ld p, %s" val + local tmp 32 + lsl val 16 tmp + and 0xFFFF p p + or tmp p p + +000000001DDD0SSS ld_ext_int + meta src internal.S + svp_write_ext D + switch D + case 7 + dis "ld al, %s" src + + default + dis "ld %s, %s" external.D src + end + +000000001DDD0011 ld_ext_a + local tmp 32 + lsr a 16 tmp + meta src tmp + svp_write_ext D + switch D + case 7 + dis "ld al, a" + + default + dis "ld %s, a" external.D + end + +000000001DDD0111 ld_ext_p + local tmp 32 + lsr p 16 tmp + meta src tmp + svp_write_ext D + switch D + case 7 + dis "ld al, p" + + default + dis "ld %s, p" external.D + end + + +000000001DDD1SSS ld_ext_ext + svp_read_ext S + meta src val + svp_write_ext D + switch D + case 7 + dis "ld al, %s" src + default + dis "ld %s, %s" external.D src + end + +0000001B0DDDMMPP ld_int_ram + svp_ram_read M B P + dis "ld %s, (%s%s)" internal.D reg modestr + mov val internal.D + +0000001B0011MMPP ld_a_ram + svp_ram_read M B P + dis "ld a, (%s%s)" reg modestr + local tmp 32 + lsl val 16 tmp + and 0xFFFF a a + or tmp a a + +0000001B0111MMPP ld_p_ram + svp_ram_read M B P + dis "ld p, (%s%s)" reg modestr + local tmp 32 + lsl val 16 tmp + and 0xFFFF p p + or tmp p p + +0000001B0101MMPP ld_st_ram + svp_ram_read M B P + dis "ld st, (%s%s)" reg modestr + svp_push val + +0100100FCCCC0000 call_cond + svp_check_cond F C + svp_op_fetch + switch istrue + case 0 + + default + svp_push pc + mov scratch1 pc + end + +0100110FCCCC0000 bra_cond + svp_check_cond F C + svp_op_fetch + switch istrue + case 0 + + default + mov scratch1 pc + end + +svp_op_fetch + cycles 1 + cmp 1024 pc + + if >=U + mov pc scratch1 + add scratch1 scratch1 scratch1 + ocall read_16 + + else + mov iram.pc scratch1 + end + add 1 pc pc + +svp_run_op + svp_op_fetch + dispatch scratch1 \ No newline at end of file