aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2023-02-11 20:49:39 +0000
committerGravatar Peter McGoron 2023-02-11 20:49:39 +0000
commit5f3275cf2d3e1f371595f3ef15831e2ee788f780 (patch)
tree97b2bbe678cc5f45aaca568ff5d4c9edd3a41005
parentmore test (diff)
add signed division IDIV
-rw-r--r--asm/creole.py12
-rw-r--r--asm/ffi.py32
-rw-r--r--asm/test.py109
-rw-r--r--creole.c14
-rw-r--r--creole.h11
5 files changed, 140 insertions, 38 deletions
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
};