diff --git a/asm/creole.py b/asm/creole.py index 2e323e4..3746003 100644 --- a/asm/creole.py +++ b/asm/creole.py @@ -11,6 +11,11 @@ def word_2c(w): def ti(w): """ Explicitly transform integer into two's compliment representation. """ return w if w >= 0 else word_wc(-w) +def from_2c(w): + """ Turn two's compliment word into Python integer. """ + if (w >> 31) & 1 == 0: + return w + return -word_2c(w) class ArgType(Enum): """ Class denoting the type of an argument to an instruction. """ @@ -86,9 +91,10 @@ class Instruction(Enum): ADD = 3, ArgType.REG, ArgType.VAL, ArgType.VAL MUL = 4, ArgType.REG, ArgType.VAL, ArgType.VAL DIV = 5, ArgType.REG, ArgType.VAL, ArgType.VAL - JL = 6, ArgType.LAB, ArgType.VAL, ArgType.VAL - CLB = 7, ArgType.LAB - SYS = 8, ArgType.VAL + IDIV = 6, ArgType.REG, ArgType.VAL, ArgType.VAL + JL = 7, ArgType.LAB, ArgType.VAL, ArgType.VAL + CLB = 8, ArgType.LAB + SYS = 9, ArgType.VAL def __init__(self, opcode, *args): if opcode > 0x7F or opcode < 0: diff --git a/asm/ffi.py b/asm/ffi.py index a1907c6..4c1ca15 100644 --- a/asm/ffi.py +++ b/asm/ffi.py @@ -1,4 +1,5 @@ from ctypes import * +import creole from enum import Enum dll = CDLL("./libcreole.so") @@ -24,6 +25,7 @@ class RunRet(Enum): RUN_LABEL_OVERFLOW = 5 REGISTER_OVERFLOW = 6 UNKNOWN_OPCODE = 7 + DIVIDE_BY_ZERO = 8 def is_halt(self): return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL) @@ -59,8 +61,30 @@ class CEnv(Structure): ("prglen", c_size_t) ] +class RegisterOverflowError(Exception): + def __init__(self, reg): + self.reg = reg +class StackOverflowError(Exception): + def __init__(self, pos): + self.pos = pos +class InvalidSyscallError(Exception): + def __init__(self, sc): + self.sc = sc +class CompileError(Exception): + def __init__(self, r): + self.r = r + class Environment: - def __init__(self, reglen=32, lablen=32, stklen=4096, prglen=4096): + def getreg(self, reg): + if reg >= self.cenv.reglen or reg < 0: + raise RegisterOverflowError(r) + return creole.from_2c(self.cenv.reg[reg]) + def getstk(self, stk): + if stk >= self.cenv.stklen or stk < 0: + raise StackOverflowError(r) + return creole.from_2c(self.cenv.stk[stk]) + + def __init__(self, prog=None, reglen=32, lablen=32, stklen=4096, prglen=4096): cenv = CEnv() cenv.reglen = reglen cenv.reg = (c_uint * reglen)() @@ -78,6 +102,10 @@ class Environment: cenv.prgend = 0 self.cenv = cenv + if prog is not None: + r = self.load(prog) + if r is not CompileRet.OK: + raise CompileError(r) def restart(self): self.cenv.stkptr = 0 @@ -91,7 +119,7 @@ class Environment: return CompileRet(ret) def syscall(self, sc): - pass + raise InvalidSyscallError(sc) def __call__(self): sc = c_size_t() ret = RunRet.CONTINUE diff --git a/asm/test.py b/asm/test.py index 1f66a4a..1274d9c 100644 --- a/asm/test.py +++ b/asm/test.py @@ -56,10 +56,8 @@ class PushTest(unittest.TestCase): p = Program() p.parse_asm_line("PUSH r0") p.parse_asm_line("PUSH 6") - b = p() - ex = ffi.Environment() - self.assertEqual(ex.load(b), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex.cenv.prgend, 2) self.assertEqual(ex.cenv.prg[0].opcode, 1) @@ -83,19 +81,17 @@ class PushTest(unittest.TestCase): stklen = 40 for n in range(0,stklen): p.parse_asm_line("PUSH 5") - ex = ffi.Environment(stklen=stklen) - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p(), stklen=stklen) self.assertEqual(ex(), ffi.RunRet.STOP) for n in range(0,stklen): - self.assertEqual(ex.cenv.stk[n], 5) + self.assertEqual(ex.getstk(n), 5) def test_push_overflow(self): p = Program() stklen = 40 for n in range(0, stklen + 1): p.parse_asm_line("PUSH 5") - ex = ffi.Environment(stklen=stklen) - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p(), stklen=stklen) self.assertEqual(ex(), ffi.RunRet.STACK_OVERFLOW) class PopTest(unittest.TestCase): @@ -104,8 +100,7 @@ class PopTest(unittest.TestCase): p.parse_asm_line("pop r9") b = p() self.assertEqual(b, b'\x02\xC2\x89\x00') - ex = ffi.Environment() - self.assertEqual(ex.load(b), ffi.CompileRet.OK) + ex = ffi.Environment(b) self.assertEqual(ex.cenv.prgend, 1) self.assertEqual(ex.cenv.prg[0].opcode, 2) @@ -152,8 +147,7 @@ class PopTest(unittest.TestCase): def test_pop_underflow(self): p = Program() p.parse_asm_line("pop r0") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW) def test_pop_underflow_2(self): @@ -161,16 +155,14 @@ class PopTest(unittest.TestCase): p.parse_asm_line("push 5") p.parse_asm_line("pop r0") p.parse_asm_line("pop r1") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW) class AddTest(unittest.TestCase): def test_exec_add(self): p = Program() p.parse_asm_line("add r0 1 1") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex.cenv.reg[0], 2) @@ -179,11 +171,10 @@ class AddTest(unittest.TestCase): p.parse_asm_line("add r0 10 20") p.parse_asm_line("add r1 5 0") p.parse_asm_line("add r1 r0 -40") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STOP) - self.assertEqual(ex.cenv.reg[0], 30) - self.assertEqual(ex.cenv.reg[1], word_2c(10)) + self.assertEqual(ex.getreg(0), 30) + self.assertEqual(ex.getreg(1), -10) def test_exec_add_throw_imm(self): p = Program() @@ -216,20 +207,18 @@ class MulTest(unittest.TestCase): def test_exec_mul_imm_imm(self): p = Program() p.parse_asm_line("mul r0 2 2") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STOP) - self.assertEqual(ex.cenv.reg[0], 4) + self.assertEqual(ex.getreg(0), 4) def test_exec_mul_imm_neg_imm(self): p = Program() p.parse_asm_line("mul r0 -5 5") p.parse_asm_line("mul r1 r0 -5") - ex = ffi.Environment() - self.assertEqual(ex.load(p()), ffi.CompileRet.OK) + ex = ffi.Environment(p()) self.assertEqual(ex(), ffi.RunRet.STOP) - self.assertEqual(ex.cenv.reg[0], word_2c(25)) - self.assertEqual(ex.cenv.reg[1], 125) + self.assertEqual(ex.getreg(0), -25) + self.assertEqual(ex.getreg(1), 125) def test_exec_mul_throw_imm(self): p = Program() @@ -258,6 +247,68 @@ class MulTest(unittest.TestCase): self.assertEqual(cm.exception.i, 2) self.assertEqual(cm.exception.opcode, 4) +class DivTest(unittest.TestCase): + def test_div(self): + p = Program() + p.parse_asm_line("div r0 8 4") + ex = ffi.Environment(p()) + self.assertEqual(ex(), ffi.RunRet.STOP) + self.assertEqual(ex.getreg(0), 2) + + def test_div_round_down(self): + p = Program() + p.parse_asm_line("div r0 8 10") + ex = ffi.Environment(p()) + self.assertEqual(ex(), ffi.RunRet.STOP) + + def test_div_by_zero(self): + p = Program() + p.parse_asm_line("div r0 8 0") + ex = ffi.Environment(p()) + self.assertEqual(ex(), ffi.RunRet.DIVIDE_BY_ZERO) + + def test_idiv_by_zero(self): + p = Program() + p.parse_asm_line("idiv r0 8 0") + ex = ffi.Environment(p()) + self.assertEqual(ex(), ffi.RunRet.DIVIDE_BY_ZERO) + + def test_div_neg(self): + p = Program() + p.parse_asm_line("idiv r0 16 -4") + p.parse_asm_line("idiv r1 r0 -4") + ex = ffi.Environment(p()) + self.assertEqual(ex(), ffi.RunRet.STOP) + self.assertEqual(ex.getreg(0), -4) + self.assertEqual(ex.getreg(1), 1) + + def test_exec_div_throw_imm(self): + p = Program() + with self.assertRaises(TypecheckException) as cm: + p.parse_asm_line("div 5 1 2") + self.assertEqual(cm.exception.argtype, ArgType.REG) + self.assertEqual(cm.exception.sarg, '5') + self.assertEqual(cm.exception.i, 0) + self.assertEqual(cm.exception.opcode, 5) + + def test_exec_div_throw_lab_1(self): + p = Program() + with self.assertRaises(TypecheckException) as cm: + p.parse_asm_line("div r0 l123 456") + self.assertEqual(cm.exception.argtype, ArgType.VAL) + self.assertEqual(cm.exception.sarg, 'l123') + self.assertEqual(cm.exception.i, 1) + self.assertEqual(cm.exception.opcode, 5) + + def test_exec_div_throw_lab_2(self): + p = Program() + with self.assertRaises(TypecheckException) as cm: + p.parse_asm_line("div r5 1919 l24") + self.assertEqual(cm.exception.argtype, ArgType.VAL) + self.assertEqual(cm.exception.sarg, 'l24') + self.assertEqual(cm.exception.i, 2) + self.assertEqual(cm.exception.opcode, 5) + class ProgramTest(unittest.TestCase): def test_exec_simple_reg(self): p = Program() @@ -268,8 +319,8 @@ class ProgramTest(unittest.TestCase): ex = ffi.Environment() self.assertEqual(ex.load(p()), ffi.CompileRet.OK) self.assertEqual(ex(), ffi.RunRet.STOP) - self.assertEqual(ex.cenv.reg[0], 6) - self.assertEqual(ex.cenv.reg[1], 5) + self.assertEqual(ex.getreg(0), 6) + self.assertEqual(ex.getreg(1), 5) if __name__ == "__main__": unittest.main() diff --git a/creole.c b/creole.c index 1fa6b11..8865939 100644 --- a/creole.c +++ b/creole.c @@ -35,6 +35,7 @@ static const struct { defop(ADD, 3, TYPE_REG, TYPE_VAL, TYPE_VAL), defop(MUL, 3, TYPE_REG, TYPE_VAL, TYPE_VAL), defop(DIV, 3, TYPE_REG, TYPE_VAL, TYPE_VAL), + defop(IDIV, 3, TYPE_REG, TYPE_VAL, TYPE_VAL), defop(JL, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL), defop(CLB, 1, TYPE_LAB, TYPE_NONE, TYPE_NONE), defop(SYS, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE) @@ -323,8 +324,9 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r) return CREOLE_OPCODE_READ_ERROR; ins->opcode = w.word; - if (w.word >= CREOLE_ARG_TYPE_LEN || w.len != 1) + if (w.word >= CREOLE_OPCODE_LEN || w.len != 1) { return CREOLE_OPCODE_MALFORMED; + } for (arg = 0; arg < opcode_info[ins->opcode].arglen; arg++) { if (!decode_seq(r, &w)) @@ -542,8 +544,18 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc) case CREOLE_DIV: check(read_val(env, ins, 1, &a1)); check(read_val(env, ins, 2, &a2)); + if (a2 == 0) + return CREOLE_DIV_BY_ZERO; check(creole_reg_write(env, ins->w[0], a1 / a2)); break; + case CREOLE_IDIV: + check(read_val(env, ins, 1, &a1)); + check(read_val(env, ins, 2, &a2)); + if (a2 == 0) + return CREOLE_DIV_BY_ZERO; + check(creole_reg_write(env, ins->w[0], + (creole_signed)a1 / (creole_signed)a2)); + break; case CREOLE_JL: check(read_val(env, ins, 1, &a1)); check(read_val(env, ins, 2, &a2)); diff --git a/creole.h b/creole.h index ba10dda..0e84a72 100644 --- a/creole.h +++ b/creole.h @@ -7,11 +7,14 @@ #ifndef CREOLE_WORD # define CREOLE_WORD unsigned int # define CREOLE_WORD_MAX UINT_MAX +# define CREOLE_SIGNED_WORD int +# define CREOLE_SIGNED_MAX INT_MAX #endif #define CREOLE_MAX_ARG 3 typedef CREOLE_WORD creole_word; +typedef CREOLE_SIGNED_WORD creole_signed; enum creole_opcode { CREOLE_NOOP = 0, @@ -20,9 +23,10 @@ enum creole_opcode { CREOLE_ADD = 3, CREOLE_MUL = 4, CREOLE_DIV = 5, - CREOLE_JL = 6, - CREOLE_CLB = 7, - CREOLE_SYS = 8, + CREOLE_IDIV = 6, + CREOLE_JL = 7, + CREOLE_CLB = 8, + CREOLE_SYS = 9, CREOLE_OPCODE_LEN }; @@ -56,6 +60,7 @@ enum creole_run_ret { CREOLE_RUN_LABEL_OVERFLOW, CREOLE_REGISTER_OVERFLOW, CREOLE_STEP_UNKNOWN_OPCODE, + CREOLE_DIV_BY_ZERO, CREOLE_RUN_RET_LEN };