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): def ti(w):
""" Explicitly transform integer into two's compliment representation. """ """ Explicitly transform integer into two's compliment representation. """
return w if w >= 0 else word_wc(-w) 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 ArgType(Enum):
""" Class denoting the type of an argument to an instruction. """ """ 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 ADD = 3, ArgType.REG, ArgType.VAL, ArgType.VAL
MUL = 4, ArgType.REG, ArgType.VAL, ArgType.VAL MUL = 4, ArgType.REG, ArgType.VAL, ArgType.VAL
DIV = 5, ArgType.REG, ArgType.VAL, ArgType.VAL DIV = 5, ArgType.REG, ArgType.VAL, ArgType.VAL
JL = 6, ArgType.LAB, ArgType.VAL, ArgType.VAL IDIV = 6, ArgType.REG, ArgType.VAL, ArgType.VAL
CLB = 7, ArgType.LAB JL = 7, ArgType.LAB, ArgType.VAL, ArgType.VAL
SYS = 8, ArgType.VAL CLB = 8, ArgType.LAB
SYS = 9, ArgType.VAL
def __init__(self, opcode, *args): def __init__(self, opcode, *args):
if opcode > 0x7F or opcode < 0: if opcode > 0x7F or opcode < 0:

View File

@ -1,4 +1,5 @@
from ctypes import * from ctypes import *
import creole
from enum import Enum from enum import Enum
dll = CDLL("./libcreole.so") dll = CDLL("./libcreole.so")
@ -24,6 +25,7 @@ class RunRet(Enum):
RUN_LABEL_OVERFLOW = 5 RUN_LABEL_OVERFLOW = 5
REGISTER_OVERFLOW = 6 REGISTER_OVERFLOW = 6
UNKNOWN_OPCODE = 7 UNKNOWN_OPCODE = 7
DIVIDE_BY_ZERO = 8
def is_halt(self): def is_halt(self):
return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL) return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL)
@ -59,8 +61,30 @@ class CEnv(Structure):
("prglen", c_size_t) ("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: 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 = CEnv()
cenv.reglen = reglen cenv.reglen = reglen
cenv.reg = (c_uint * reglen)() cenv.reg = (c_uint * reglen)()
@ -78,6 +102,10 @@ class Environment:
cenv.prgend = 0 cenv.prgend = 0
self.cenv = cenv self.cenv = cenv
if prog is not None:
r = self.load(prog)
if r is not CompileRet.OK:
raise CompileError(r)
def restart(self): def restart(self):
self.cenv.stkptr = 0 self.cenv.stkptr = 0
@ -91,7 +119,7 @@ class Environment:
return CompileRet(ret) return CompileRet(ret)
def syscall(self, sc): def syscall(self, sc):
pass raise InvalidSyscallError(sc)
def __call__(self): def __call__(self):
sc = c_size_t() sc = c_size_t()
ret = RunRet.CONTINUE ret = RunRet.CONTINUE

View File

@ -56,10 +56,8 @@ class PushTest(unittest.TestCase):
p = Program() p = Program()
p.parse_asm_line("PUSH r0") p.parse_asm_line("PUSH r0")
p.parse_asm_line("PUSH 6") p.parse_asm_line("PUSH 6")
b = p()
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(b), ffi.CompileRet.OK)
self.assertEqual(ex.cenv.prgend, 2) self.assertEqual(ex.cenv.prgend, 2)
self.assertEqual(ex.cenv.prg[0].opcode, 1) self.assertEqual(ex.cenv.prg[0].opcode, 1)
@ -83,19 +81,17 @@ class PushTest(unittest.TestCase):
stklen = 40 stklen = 40
for n in range(0,stklen): for n in range(0,stklen):
p.parse_asm_line("PUSH 5") p.parse_asm_line("PUSH 5")
ex = ffi.Environment(stklen=stklen) ex = ffi.Environment(p(), stklen=stklen)
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex(), ffi.RunRet.STOP)
for n in range(0,stklen): for n in range(0,stklen):
self.assertEqual(ex.cenv.stk[n], 5) self.assertEqual(ex.getstk(n), 5)
def test_push_overflow(self): def test_push_overflow(self):
p = Program() p = Program()
stklen = 40 stklen = 40
for n in range(0, stklen + 1): for n in range(0, stklen + 1):
p.parse_asm_line("PUSH 5") p.parse_asm_line("PUSH 5")
ex = ffi.Environment(stklen=stklen) ex = ffi.Environment(p(), stklen=stklen)
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STACK_OVERFLOW) self.assertEqual(ex(), ffi.RunRet.STACK_OVERFLOW)
class PopTest(unittest.TestCase): class PopTest(unittest.TestCase):
@ -104,8 +100,7 @@ class PopTest(unittest.TestCase):
p.parse_asm_line("pop r9") p.parse_asm_line("pop r9")
b = p() b = p()
self.assertEqual(b, b'\x02\xC2\x89\x00') self.assertEqual(b, b'\x02\xC2\x89\x00')
ex = ffi.Environment() ex = ffi.Environment(b)
self.assertEqual(ex.load(b), ffi.CompileRet.OK)
self.assertEqual(ex.cenv.prgend, 1) self.assertEqual(ex.cenv.prgend, 1)
self.assertEqual(ex.cenv.prg[0].opcode, 2) self.assertEqual(ex.cenv.prg[0].opcode, 2)
@ -152,8 +147,7 @@ class PopTest(unittest.TestCase):
def test_pop_underflow(self): def test_pop_underflow(self):
p = Program() p = Program()
p.parse_asm_line("pop r0") p.parse_asm_line("pop r0")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW) self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW)
def test_pop_underflow_2(self): def test_pop_underflow_2(self):
@ -161,16 +155,14 @@ class PopTest(unittest.TestCase):
p.parse_asm_line("push 5") p.parse_asm_line("push 5")
p.parse_asm_line("pop r0") p.parse_asm_line("pop r0")
p.parse_asm_line("pop r1") p.parse_asm_line("pop r1")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW) self.assertEqual(ex(), ffi.RunRet.STACK_UNDERFLOW)
class AddTest(unittest.TestCase): class AddTest(unittest.TestCase):
def test_exec_add(self): def test_exec_add(self):
p = Program() p = Program()
p.parse_asm_line("add r0 1 1") p.parse_asm_line("add r0 1 1")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex(), ffi.RunRet.STOP)
self.assertEqual(ex.cenv.reg[0], 2) 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 r0 10 20")
p.parse_asm_line("add r1 5 0") p.parse_asm_line("add r1 5 0")
p.parse_asm_line("add r1 r0 -40") p.parse_asm_line("add r1 r0 -40")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex(), ffi.RunRet.STOP)
self.assertEqual(ex.cenv.reg[0], 30) self.assertEqual(ex.getreg(0), 30)
self.assertEqual(ex.cenv.reg[1], word_2c(10)) self.assertEqual(ex.getreg(1), -10)
def test_exec_add_throw_imm(self): def test_exec_add_throw_imm(self):
p = Program() p = Program()
@ -216,20 +207,18 @@ class MulTest(unittest.TestCase):
def test_exec_mul_imm_imm(self): def test_exec_mul_imm_imm(self):
p = Program() p = Program()
p.parse_asm_line("mul r0 2 2") p.parse_asm_line("mul r0 2 2")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) 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): def test_exec_mul_imm_neg_imm(self):
p = Program() p = Program()
p.parse_asm_line("mul r0 -5 5") p.parse_asm_line("mul r0 -5 5")
p.parse_asm_line("mul r1 r0 -5") p.parse_asm_line("mul r1 r0 -5")
ex = ffi.Environment() ex = ffi.Environment(p())
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex(), ffi.RunRet.STOP)
self.assertEqual(ex.cenv.reg[0], word_2c(25)) self.assertEqual(ex.getreg(0), -25)
self.assertEqual(ex.cenv.reg[1], 125) self.assertEqual(ex.getreg(1), 125)
def test_exec_mul_throw_imm(self): def test_exec_mul_throw_imm(self):
p = Program() p = Program()
@ -258,6 +247,68 @@ class MulTest(unittest.TestCase):
self.assertEqual(cm.exception.i, 2) self.assertEqual(cm.exception.i, 2)
self.assertEqual(cm.exception.opcode, 4) 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): class ProgramTest(unittest.TestCase):
def test_exec_simple_reg(self): def test_exec_simple_reg(self):
p = Program() p = Program()
@ -268,8 +319,8 @@ class ProgramTest(unittest.TestCase):
ex = ffi.Environment() ex = ffi.Environment()
self.assertEqual(ex.load(p()), ffi.CompileRet.OK) self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP) self.assertEqual(ex(), ffi.RunRet.STOP)
self.assertEqual(ex.cenv.reg[0], 6) self.assertEqual(ex.getreg(0), 6)
self.assertEqual(ex.cenv.reg[1], 5) self.assertEqual(ex.getreg(1), 5)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -35,6 +35,7 @@ static const struct {
defop(ADD, 3, TYPE_REG, TYPE_VAL, TYPE_VAL), defop(ADD, 3, TYPE_REG, TYPE_VAL, TYPE_VAL),
defop(MUL, 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(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(JL, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL),
defop(CLB, 1, TYPE_LAB, TYPE_NONE, TYPE_NONE), defop(CLB, 1, TYPE_LAB, TYPE_NONE, TYPE_NONE),
defop(SYS, 1, TYPE_VAL, 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; return CREOLE_OPCODE_READ_ERROR;
ins->opcode = w.word; 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; return CREOLE_OPCODE_MALFORMED;
}
for (arg = 0; arg < opcode_info[ins->opcode].arglen; arg++) { for (arg = 0; arg < opcode_info[ins->opcode].arglen; arg++) {
if (!decode_seq(r, &w)) 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: case CREOLE_DIV:
check(read_val(env, ins, 1, &a1)); check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2)); 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)); check(creole_reg_write(env, ins->w[0], a1 / a2));
break; 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: case CREOLE_JL:
check(read_val(env, ins, 1, &a1)); check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2)); check(read_val(env, ins, 2, &a2));

View File

@ -7,11 +7,14 @@
#ifndef CREOLE_WORD #ifndef CREOLE_WORD
# define CREOLE_WORD unsigned int # define CREOLE_WORD unsigned int
# define CREOLE_WORD_MAX UINT_MAX # define CREOLE_WORD_MAX UINT_MAX
# define CREOLE_SIGNED_WORD int
# define CREOLE_SIGNED_MAX INT_MAX
#endif #endif
#define CREOLE_MAX_ARG 3 #define CREOLE_MAX_ARG 3
typedef CREOLE_WORD creole_word; typedef CREOLE_WORD creole_word;
typedef CREOLE_SIGNED_WORD creole_signed;
enum creole_opcode { enum creole_opcode {
CREOLE_NOOP = 0, CREOLE_NOOP = 0,
@ -20,9 +23,10 @@ enum creole_opcode {
CREOLE_ADD = 3, CREOLE_ADD = 3,
CREOLE_MUL = 4, CREOLE_MUL = 4,
CREOLE_DIV = 5, CREOLE_DIV = 5,
CREOLE_JL = 6, CREOLE_IDIV = 6,
CREOLE_CLB = 7, CREOLE_JL = 7,
CREOLE_SYS = 8, CREOLE_CLB = 8,
CREOLE_SYS = 9,
CREOLE_OPCODE_LEN CREOLE_OPCODE_LEN
}; };
@ -56,6 +60,7 @@ enum creole_run_ret {
CREOLE_RUN_LABEL_OVERFLOW, CREOLE_RUN_LABEL_OVERFLOW,
CREOLE_REGISTER_OVERFLOW, CREOLE_REGISTER_OVERFLOW,
CREOLE_STEP_UNKNOWN_OPCODE, CREOLE_STEP_UNKNOWN_OPCODE,
CREOLE_DIV_BY_ZERO,
CREOLE_RUN_RET_LEN CREOLE_RUN_RET_LEN
}; };