add signed division IDIV

This commit is contained in:
Peter McGoron 2023-02-11 20:49:39 +00:00
parent 18ad8f03c6
commit 5f3275cf2d
5 changed files with 140 additions and 38 deletions

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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));

View File

@ -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
};