From 13adbbf68c2183bad9c89659c9a8d7d01ce2d1a6 Mon Sep 17 00:00:00 2001 From: Loic Nageleisen Date: Fri, 27 Apr 2012 17:07:05 +0200 Subject: [PATCH] WIP working towards 1.5 --- dcpu_16.py | 368 +++++++++++++++++++++++++++++++++++++---------------- test.py | 152 +++++++++++++--------- 2 files changed, 350 insertions(+), 170 deletions(-) diff --git a/dcpu_16.py b/dcpu_16.py index b0e60c1..b47193e 100644 --- a/dcpu_16.py +++ b/dcpu_16.py @@ -5,12 +5,19 @@ Spec: http://0x10c.com/doc/dcpu-16.txt See LICENSE for licensing information. """ +# TODO: cycle count management +# branching opcodes take one cycle longer to perform if the test fails +# when they skip an if instruction, they will skip an additional instruction +# TODO: signed functions +# signed numbers are represented using two's complement +# TODO: interrupts +# TODO: hardware from __future__ import print_function, division # DCPU-16 spec version -spec = '1.1' +spec = '1.5' # log tool @@ -100,131 +107,247 @@ def pointerize(f): return ptrz -@opcode(0x0, 0x01) +@opcode(0x00, 0x01) def JSR(c, a): """pushes the address of the next instruction to the stack, then sets PC to a""" c.sp -= 1 c.m[c.sp] = c.pc c.pc = a() -@opcode(0x1) -def SET(c, a, b): - """sets a to b""" - a.set(b()) +@opcode(0x00, 0x07) +def HCF(c, a): + """use sparingly""" + pass -@opcode(0x2) -def ADD(c, a, b): - """sets a to a+b, sets O to 0x0001 if there's an overflow""" - res = (a() + b()) +@opcode(0x00, 0x08) +def INT(c, a): + """triggers a software interrupt with message a""" + pass + +@opcode(0x00, 0x09) +def IAG(c, a): + """sets a to IA""" + pass + +@opcode(0x00, 0x0A) +def IAS(c, a): + """sets IA to a""" + pass + +@opcode(0x00, 0x0B) +def IAP(c, a): + """if IA is 0, does nothing, otherwise pushes IA to the stack, then sets IA to a""" + pass + +@opcode(0x00, 0x0C) +def IAQ(c, a): + """if a is nonzero, interrupts will be added to the queue instead of triggered. if a is zero, interrupts will be triggered as nomral again""" + pass + +@opcode(0x00, 0x10) +def HWN(c, a): + """sets a to number of connected hardware devices""" + pass + +@opcode(0x00, 0x11) +def HWQ(c, a): + """sets A, B, C,X, Y registers to information about hardware a + + A+(B<<16) is a 32 bit word identifying the hardware id + C is the hardware version + X+(Y<<16) is a 32 bit word identifying the manufacturer + """ + pass + +@opcode(0x00, 0x12) +def HWI(c, a): + """sends an interrupt to hardware a""" + pass + +@opcode(0x01) +def SET(c, b, a): + """sets b to a""" + b.set(a()) + +@opcode(0x02) +def ADD(c, b, a): + """sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise""" + res = (b() + a()) if res > wmask: - c.o = 0x0001 + c.ex = 0x0001 res = res & wmask - a.set(res) + b.set(res) -@opcode(0x3) -def SUB(c, a, b): - """sets a to a-b, sets O to 0xFFFF if there's an underflow""" - res = (a() - b()) +@opcode(0x03) +def SUB(c, b, a): + """sets b to b-a, sets EX to 0xFFFF if there's an underflow, 0x0 otherwise""" + res = (b() - a()) if res & (wmask+1): - c.o = 0xFFFF + c.ex = 0xFFFF res = res & wmask - a.set(res) + b.set(res) -@opcode(0x4) -def MUL(c, a, b): - """sets a to a*b, sets O to ((a*b)>>16)&0xFFFF""" - res = (a() * b()) - c.o = ((a() * b()) >> w) & wmask +@opcode(0x04) +def MUL(c, b, a): + """sets b to b*a, sets EX to ((b*a)>>16)&0xFFFF (treats b, a as unsigned)""" + res = (b() * a()) + c.ex = ((b() * a()) >> w) & wmask res = res & wmask - a.set(res) + b.set(res) -@opcode(0x5) -def DIV(c, a, b): - """sets a to a/b, sets O to ((a<<16)/b)&0xFFFF""" - res = a() // b() - c.o = ((a() << w) // b()) & wmask - a.set(res) +@opcode(0x05) +def MLI(c, b, a): + """like MUL, but treats b, a as signed""" + pass -@opcode(0x6) -def MOD(c, a, b): - """sets a to a%b. if b==0, sets a to 0 instead""" - if b()==0: +@opcode(0x06) +def DIV(c, b, a): + """sets b to b/a, sets EX to ((b<<16)/a)&0xFFFF. if a==0, sets b and EX to 0 instead. (treats b, a as unsigned)""" + res = b() // a() + c.ex = ((b() << w) // a()) & wmask + b.set(res) + +@opcode(0x07) +def DVI(c, b, a): + """like DIV, but treats b, a as signed. Rounds towards 0""" + pass + +@opcode(0x08) +def MOD(c, b, a): + """sets b to b%a. if a==0 sets b to 0 instead""" + if a()==0: res = 0 else: - res = a() % b() - a.set(res) + res = b() % a() + b.set(res) -@opcode(0x7) -def SHL(c, a, b): - """sets a to a<>16)&0xFFFF""" - res = (a() << b()) - c.o = ((a() << b()) >> w) & wmask +@opcode(0x09) +def MDI(c, b, a): + """like MOD, but treat b, a as signed. Rounds towards 0""" + pass + +@opcode(0x0A) +def AND(c, b, a): + """sets b to b&a""" + res = b() & a() + b.set(res) + +@opcode(0x0B) +def BOR(c, b, a): + """sets b to b|a""" + res = b() | a() + b.set(res) + +@opcode(0x0C) +def XOR(c, b, a): + """sets b to b^a""" + res = b() ^ a() + b.set(res) + +@opcode(0x0D) +def SHR(c, b, a): + """sets b to b>>>a. sets EX to ((b<<16)>>a)&0xFFFF (logical shift)""" + res = (b() >> a()) + c.ex = ((b() << w) >> a()) & wmask res = res & wmask - a.set(res) + b.set(res) -@opcode(0x8) -def SHR(c, a, b): - """sets a to a>>b. sets O to ((a>>16)>>b)&0xFFFF""" - res = (a() >> b()) - c.o = ((a() >> w) >> b()) & wmask +@opcode(0x0E) +def ASR(c, b, a): + """sets b to b>>a. sets EX to ((b<<16)>>>a)&0xFFFF (arithmetic shift) (treats b as signed)""" + pass + +@opcode(0x0F) +def SHL(c, b, a): + """sets b to b<>16)&0xFFFF""" + res = (b() << a()) + c.ex = ((b() << a()) >> w) & wmask res = res & wmask - a.set(res) + b.set(res) -@opcode(0x9) -def AND(c, a, b): - """sets a to a&b""" - res = a() & b() - a.set(res) - -@opcode(0xA) -def BOR(c, a, b): - """sets a to a|b""" - res = a() | b() - a.set(res) - -@opcode(0xB) -def XOR(c, a, b): - """sets a to a^b""" - res = a() ^ b() - a.set(res) - -@opcode(0xC) -def IFE(c, a, b): - """performs next instruction only if a==b""" - if a() == b(): +@opcode(0x10) +def IFB(c, b, a): + """performs next instruction only if (b&a)!=0""" + if (b() & a()) != 0: c.skip = False else: c.skip = True -@opcode(0xD) -def IFN(c, a, b): - """performs next instruction only if a!=b""" - if a() != b(): +@opcode(0x11) +def IFC(c, b, a): + """performs next instruction only if (b&a)==0""" + if (b() & a()) == 0: c.skip = False else: c.skip = True -@opcode(0xE) -def IFG(c, a, b): - """performs next instruction only if a>b""" - if a() > b(): +@opcode(0x12) +def IFE(c, b, a): + """performs next instruction only if b==a""" + if b() == a(): c.skip = False else: c.skip = True -@opcode(0xF) -def IFB(c, a, b): - """performs next instruction only if (a&b)!=0""" - if (a() & b()) != 0: +@opcode(0x13) +def IFN(c, b, a): + """performs next instruction only if b!=a""" + if b() != a(): c.skip = False else: c.skip = True +@opcode(0x14) +def IFG(c, b, a): + """performs next instruction only if b>a""" + if b() > a(): + c.skip = False + else: + c.skip = True + +@opcode(0x15) +def IFA(c, b, a): + """performs next instruction only if b>a (signed)""" + pass + +@opcode(0x16) +def IFL(c, b, a): + """performs next instruction only if b> 4 & 0x3F - b_code = word >> 10 & 0x3F + opcode = word & 0x1F + b_code = word >> 5 & 0x1F + a_code = word >> 10 & 0x3F try: op = _opcode_map[opcode] try: op = op[a_code] except TypeError: - args = (c._pointer(a_code), - c._pointer(b_code)) + args = (c._pointer(b_code), + c._pointer(a_code)) else: - args = (c._pointer(b_code),) + args = (c._pointer(a_code),) finally: if c.debug: log(' '.join([op.__name__] + [arg.codestr for arg in args])) @@ -428,9 +557,9 @@ class CPU(object): (["A", "B", "C", "X", "Y", "Z", "I", "J", - "PC", "SP", "O", + "PC", "SP", "EX", ][i], - (c.r + [c.pc, c.sp, c.o])[i]) + (c.r + [c.pc, c.sp, c.ex])[i]) for i in range(11)) def load_m(c, data=None, io=None): @@ -446,8 +575,25 @@ class CPU(object): spec_demo = [ - 0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d, - 0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463, - 0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a, - 0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000, + # TODO: hand-reassembled for 1.5, but still some bugs + 0x7c01, 0x0030, # SET A, 0x30 + 0x7fc1, 0x1000, 0x0020, # SET [0x1000], 0x20 + 0x7803, 0x1000, # SUB A, [0X1000] + 0xc012, # 0xc013 # IFE A, 0xF + 0x7f81, 0x001a, # SET PC, crash + 0x90c1, # 0xacc1 # SET I, 10 + 0x7c01, 0x2000, # SET A, 0x2000 + # :loop + 0x22C1, 0x2000, # SET [I + 0x2000], [A] + 0x88C3, # SUB I, 1 + 0x84D3, # IFN I, 0 + 0x7f81, 0x000d, # SET PC, loop + 0x9461, # SET X, 4 + 0x7c20, 0x0018, # JSR testsub + 0x7f81, 0x001a, # SET PC, crash + # :testsub + 0x946f, # SHL X, 4 + 0x6381, # SET PC, POP + # :crash + 0x7f81, 0x001a, # SET PC, crash ] diff --git a/test.py b/test.py index 640601c..2463d75 100644 --- a/test.py +++ b/test.py @@ -10,7 +10,7 @@ class TestInstructions(unittest.TestCase): pass def test_SET(self): - dcpu_16.SET.opcode = (0x1,) + self.assertEqual(dcpu_16.SET.opcode, (0x01,)) c = CPU() c.a = 0x0 c.b = 0x42 @@ -19,107 +19,161 @@ class TestInstructions(unittest.TestCase): self.assertEqual(c.b, 0x42) def test_ADD(self): - dcpu_16.SET.opcode = (0x2,) + self.assertEqual(dcpu_16.ADD.opcode, (0x02,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.ADD(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17+0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) def test_SUB(self): - dcpu_16.SET.opcode = (0x3,) + self.assertEqual(dcpu_16.SUB.opcode, (0x03,)) c = CPU() c.a = 0x42 c.b = 0x17 dcpu_16.SUB(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x42-0x17) self.assertEqual(c.b, 0x17) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) def test_MUL(self): - dcpu_16.SET.opcode = (0x4,) + self.assertEqual(dcpu_16.MUL.opcode, (0x04,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.MUL(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17*0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) + + def test_MLI(self): + self.assertEqual(dcpu_16.MLI.opcode, (0x05,)) + c = CPU() + c.a = 0x17 + c.b = 0x42 + dcpu_16.MLI(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17*0x42) + self.assertEqual(c.b, 0x42) + self.assertEqual(c.ex, 0x0) def test_DIV(self): - dcpu_16.SET.opcode = (0x5,) + self.assertEqual(dcpu_16.DIV.opcode, (0x06,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.DIV(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17/0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, ((0x17<<16)/0x42)&0xFFFF) + self.assertEqual(c.ex, ((0x17<<16)/0x42)&0xFFFF) + + def test_DVI(self): + self.assertEqual(dcpu_16.DVI.opcode, (0x07,)) + c = CPU() + c.a = 0x17 + c.b = 0x42 + dcpu_16.DIV(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17/0x42) + self.assertEqual(c.b, 0x42) + self.assertEqual(c.ex, ((0x17<<16)/0x42)&0xFFFF) def test_MOD(self): - dcpu_16.SET.opcode = (0x6,) + self.assertEqual(dcpu_16.MOD.opcode, (0x08,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.MOD(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17%0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) - def test_SHL(self): - dcpu_16.SET.opcode = (0x7,) - c = CPU() - c.a = 0x17 - c.b = 0x4 - dcpu_16.SHL(c, c._pointer(0x0), c._pointer(0x1)) - self.assertEqual(c.a, 0x17<<0x4 & dcpu_16.wmask) - self.assertEqual(c.b, 0x4) - self.assertEqual(c.o, 0x0) - - def test_SHR(self): - dcpu_16.SET.opcode = (0x8,) + def test_MDI(self): + self.assertEqual(dcpu_16.MDI.opcode, (0x09,)) c = CPU() c.a = 0x17 c.b = 0x42 - dcpu_16.SHR(c, c._pointer(0x0), c._pointer(0x1)) - self.assertEqual(c.a, 0x17>>0x42) + dcpu_16.MOD(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17%0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) def test_AND(self): - dcpu_16.SET.opcode = (0x9,) + self.assertEqual(dcpu_16.AND.opcode, (0x0A,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.AND(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17&0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) def test_BOR(self): - dcpu_16.SET.opcode = (0xA,) + self.assertEqual(dcpu_16.BOR.opcode, (0x0B,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.BOR(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17|0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) def test_XOR(self): - dcpu_16.SET.opcode = (0xB,) + self.assertEqual(dcpu_16.XOR.opcode, (0x0C,)) c = CPU() c.a = 0x17 c.b = 0x42 dcpu_16.XOR(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.a, 0x17^0x42) self.assertEqual(c.b, 0x42) - self.assertEqual(c.o, 0x0) + self.assertEqual(c.ex, 0x0) + + def test_SHR(self): + self.assertEqual(dcpu_16.SHR.opcode, (0x0D,)) + c = CPU() + c.a = 0x17 + c.b = 0x42 + dcpu_16.SHR(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17>>0x42) + self.assertEqual(c.b, 0x42) + self.assertEqual(c.ex, 0x0) + + def test_ASR(self): + self.assertEqual(dcpu_16.ASR.opcode, (0x0E,)) + c = CPU() + c.a = 0x17 + c.b = 0x42 + dcpu_16.ASR(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17>>0x42) + self.assertEqual(c.b, 0x42) + self.assertEqual(c.ex, 0x0) + + def test_SHL(self): + self.assertEqual(dcpu_16.SHL.opcode, (0x0F,)) + c = CPU() + c.a = 0x17 + c.b = 0x4 + dcpu_16.SHL(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.a, 0x17<<0x4 & dcpu_16.wmask) + self.assertEqual(c.b, 0x4) + self.assertEqual(c.ex, 0x0) + + def test_IFB(self): + self.assertEqual(dcpu_16.IFB.opcode, (0x10,)) + c = CPU() + + c.a = 0xF0F0 + c.b = 0x0F0F + dcpu_16.IFB(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.skip, True) + + c.a = 0xF1F0 + c.b = 0x0F0F + dcpu_16.IFB(c, c._pointer(0x0), c._pointer(0x1)) + self.assertEqual(c.skip, False) def test_IFE(self): - dcpu_16.SET.opcode = (0xC,) + self.assertEqual(dcpu_16.IFE.opcode, (0x12,)) c = CPU() c.a = 0x17 @@ -133,7 +187,7 @@ class TestInstructions(unittest.TestCase): self.assertEqual(c.skip, False) def test_IFN(self): - dcpu_16.SET.opcode = (0xD,) + self.assertEqual(dcpu_16.IFN.opcode, (0x13,)) c = CPU() c.a = 0x17 @@ -147,7 +201,7 @@ class TestInstructions(unittest.TestCase): self.assertEqual(c.skip, True) def test_IFG(self): - dcpu_16.SET.opcode = (0xE,) + self.assertEqual(dcpu_16.IFG.opcode, (0x14,)) c = CPU() c.a = 0x41 @@ -165,22 +219,8 @@ class TestInstructions(unittest.TestCase): dcpu_16.IFG(c, c._pointer(0x0), c._pointer(0x1)) self.assertEqual(c.skip, False) - def test_IFB(self): - dcpu_16.SET.opcode = (0xF,) - c = CPU() - - c.a = 0xF0F0 - c.b = 0x0F0F - dcpu_16.IFB(c, c._pointer(0x0), c._pointer(0x1)) - self.assertEqual(c.skip, True) - - c.a = 0xF1F0 - c.b = 0x0F0F - dcpu_16.IFB(c, c._pointer(0x0), c._pointer(0x1)) - self.assertEqual(c.skip, False) - def test_JSR(self): - dcpu_16.JSR.opcode = (0x0, 0x1) + self.assertEqual(dcpu_16.JSR.opcode, (0x00, 0x01)) c = CPU() c.a = 0xDEAD @@ -203,7 +243,7 @@ class TestCPU(unittest.TestCase): for r in c.r: self.assertEqual(r, 0) self.assertEqual(c.pc, 0) - self.assertEqual(c.o, 0) + self.assertEqual(c.ex, 0) self.assertEqual(c.sp, 0) self.assertEqual(c.skip, False) self.assertEqual(c.pc, 0) @@ -219,7 +259,7 @@ class TestCPU(unittest.TestCase): for r in c.r: self.assertEqual(r, 0) self.assertEqual(c.pc, 0) - self.assertEqual(c.o, 0) + self.assertEqual(c.ex, 0) self.assertEqual(c.sp, 0) self.assertEqual(c.skip, False) self.assertEqual(c.pc, 0) @@ -239,13 +279,7 @@ class TestCPUWithPrograms(unittest.TestCase): def test_spec_demo(self): c = CPU() - data = [ - 0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d, - 0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463, - 0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a, - 0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000, - ] - c.load_m(data=data) + c.load_m(data=dcpu_16.spec_demo) self.assertEqual(c.pc, 0) c.step() # SET A, 0x30