add signed division IDIV
This commit is contained in:
parent
18ad8f03c6
commit
5f3275cf2d
|
@ -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:
|
||||
|
|
32
asm/ffi.py
32
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
|
||||
|
|
109
asm/test.py
109
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()
|
||||
|
|
14
creole.c
14
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));
|
||||
|
|
11
creole.h
11
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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue