diff --git a/ebpfcat/__init__.py b/ebpfcat/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a0d66d69d155d5ddd284bbd3cdeca1261523d13 100644
--- a/ebpfcat/__init__.py
+++ b/ebpfcat/__init__.py
@@ -0,0 +1,17 @@
+# ebpfcat, A Python-based EBPF generator and EtherCAT master
+# Copyright (C) 2021 Martin Teichmann <martin.teichmann@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
diff --git a/ebpfcat/bpf.py b/ebpfcat/bpf.py
index bea41006debee46a4d3c1bc4a60e168b55c1dfd4..b9985af20f10f491f688f6f0100043d6e650f89f 100644
--- a/ebpfcat/bpf.py
+++ b/ebpfcat/bpf.py
@@ -152,10 +152,9 @@ def prog_test_run(fd, data_in, data_out, ctx_in, ctx_out,
         ctx_in = create_string_buffer(ctx_in, len(ctx_in))
     data_out = create_string_buffer(data_out)
     ctx_out = create_string_buffer(ctx_out)
-    ret, (_, retval, _, _, _, _, _, duration, _, _, _, _) = bpf(
-            10, "IIIIQQIIIIQQ20x", fd, 0, len(data_in), len(data_out),
-            addrof(data_in), addrof(data_out), repeat, 0, 0, 0, 0, 0)
-            #len(ctx_in), len(ctx_out), addrof(ctx_in), addrof(ctx_out))
+    ret, (_, retval, _, _, _, _, _, duration) = bpf(
+            10, "IIIIQQII20x", fd, 0, len(data_in), len(data_out),
+            addrof(data_in), addrof(data_out), repeat, 0)
     return ret, retval, duration, data_out.value, ctx_out.value
 
 if __name__ == "__main__":
diff --git a/ebpfcat/chem.py b/ebpfcat/chem.py
deleted file mode 100644
index 4ac4f3e4261087bf1e5a0051f2a65fc4ba1373c6..0000000000000000000000000000000000000000
--- a/ebpfcat/chem.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# ebpfcat, A Python-based EBPF generator and EtherCAT master
-# Copyright (C) 2021 Martin Teichmann <martin.teichmann@gmail.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-from asyncio import gather, sleep, ensure_future
-from struct import unpack, pack
-from .terminals import EL7041, EL5042, Skip
-from .devices import Motor, Counter, AnalogInput
-from .ebpfcat import FastEtherCat, FastSyncGroup, SyncGroup
-
-con = Skip()
-tm1 = EL7041()
-te12 = EL5042()
-tm2 = EL7041()
-
-ec = FastEtherCat("enp0s31f6", [con, tm1, te12, tm2])
-
-
-async def monitor(ec):
-    while True:
-        print("M", ec.ebpf.count, ec.ebpf.allcount, await tin.get_state())
-        await sleep(0.1)
-
-
-async def main():
-    await ec.connect()
-    await ec.scan_bus()
-    #ensure_future(monitor(ec))
-
-    print("S", await te12.set_state(2))
-    for i in [1, 2, 3] + list(range(0x11, 0x19)):
-        print(f"P{i:x}", await te12.sdo_read(0x8018, i))
-    await te12.sdo_write(b"\0", 0x8018, 0x2)  # statusbits
-    await te12.sdo_write(b"\0", 0x8018, 0x15)  # multi
-    #await te12.sdo_write(b"\x1a", 0x8018, 0x16)  # singleturn
-    await te12.sdo_write(b" ", 0x8018, 0x16)  # singleturn
-
-    await tm2.set_state(2)
-    await tm2.sdo_write(pack("<H", 2500), 0x8010, 0x1)  # current
-    await tm2.sdo_write(pack("<H", 24000), 0x8010, 0x3)  # voltage
-
-    for i in [1, 2, 3]:
-        print(f"S{i:x}", await te12.sdo_read(0xB018, i))
-    print(f"Pos", await te12.sdo_read(0x6010, 0x11))
-    print(f"V", await te12.sdo_read(0xA018, 0x5))
-    print(f"M", await te12.sdo_read(0x10F3, 0x6))
-    print("State", await te12.get_state())
-
-    print("S", await tm2.set_state(2))
-    for i in [1,2,3,4,5,6,7,9,0x10,0x11]:
-        print(f"M{i:x}", unpack("H", await tm2.sdo_read(0x8010, i))[0])
-
-
-    m1 = Motor()
-    m1.velocity = tm2.velocity
-    m1.encoder = te12.channel2.position
-    m1.enable = tm2.enable
-
-    aie = AnalogInput(te12.channel2.status)
-    aim = AnalogInput(tm2.status)
-
-    fsg = SyncGroup(ec, [m1, aie, aim])
-
-    m1.max_velocity = 10000
-    m1.proportional = 23
-    m1.target = 185525000
-
-    print("do start")
-    fsg.start()
-    print("did start")
-
-    print(f"P0", unpack("I", await tm2.sdo_read(0x6010, 0x14))[0])
-    await sleep(0.1)
-    m1.set_enable = True
-    for i in range(20):
-        print(f"M {m1.current_position:12} {m1.velocity:5} {aie.value:4x} {aim.value:4x}")
-        await sleep(0.2)
-    m1.set_enable = False
-    m1.max_velocity = 0
-    for i in range(20):
-        print(f"S {m1.current_position:12} {aie.value:4x} {aim.value:4x}")
-        await sleep(0.02)
-    print(f"P1", unpack("I", await tm2.sdo_read(0x6010, 0x14))[0])
-
-if __name__ == "__main__":
-    from asyncio import get_event_loop
-    loop = get_event_loop()
-    loop.run_until_complete(main())
diff --git a/ebpfcat/ebpf_test.py b/ebpfcat/ebpf_test.py
index a15f8d0c37f9694912b0447bc5d2fd3073e45c46..6cea964a6aaf658a350f50a49024724411db091b 100644
--- a/ebpfcat/ebpf_test.py
+++ b/ebpfcat/ebpf_test.py
@@ -372,7 +372,6 @@ class Tests(TestCase):
             e.r2 = 5
         with cond.Else():
             e.r3 = 7
-        self.maxDiff = None
         self.assertEqual(e.opcodes, [
             Instruction(opcode=O.JLE, dst=2, src=0, off=2, imm=3),
             Instruction(opcode=O.JLE, dst=3, src=0, off=1, imm=2),
@@ -394,7 +393,6 @@ class Tests(TestCase):
         with cond.Else():
             e.r3 = 7
             e.r4 = 3
-        self.maxDiff = None
         self.assertEqual(e.opcodes, [
             Instruction(opcode=O.JGT, dst=2, src=0, off=1, imm=3),
             Instruction(opcode=O.JLE, dst=3, src=0, off=1, imm=2),
@@ -588,7 +586,6 @@ class Tests(TestCase):
             e.r8 = e.r1
 
     def test_binary_alloc(self):
-        self.maxDiff = None
         e = EBPF()
         e.r3 = e.r1 - (2 * e.r10)
         e.mH[e.r10 - 10] = 2 * e.r3
@@ -696,7 +693,7 @@ class KernelTests(TestCase):
         e.a += 7
         e.exit()
 
-        fd = e.load()
+        fd, _ = e.load(log_level=1)
         prog_test_run(fd, 1000, 1000, 0, 0, 1)
         e.a *= 2
         prog_test_run(fd, 1000, 1000, 0, 0, 1)
@@ -757,12 +754,14 @@ class KernelTests(TestCase):
         self.assertEqual(s2.bw, 69)
 
     def test_minimal(self):
-        class Global(XDP):
-            map = HashMap()
-            a = map.globalVar()
+        class Local(EBPF):
+            a = LocalVar('I')
 
-        e = Global(license="GPL")
-        e.a += 1
+        e = Local(ProgType.XDP, "GPL")
+        e.a = 7
+        e.a += 3
+        e.mI[e.r1] += e.r1
+        e.a -= 3
         e.exit()
         print(e.opcodes)
         print(e.load(log_level=1)[1])
diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py
index 488fe2bee0ec9ef745dd429621feb2fd1d7c0019..ba228b64aa665cfe3e33698b64e0cd845a87f1ec 100644
--- a/ebpfcat/ebpfcat.py
+++ b/ebpfcat/ebpfcat.py
@@ -276,7 +276,10 @@ class EtherXDP(XDP):
                     with exceed.Else():
                         self.exit(XDPExitCode.TX)
             self.r2 = self.get_fd(self.programs)
+            for i, o in enumerate(self.opcodes):
+                print(i, o)
             self.call(FuncId.tail_call)
+
         self.exit(XDPExitCode.PASS)
 
 
@@ -319,6 +322,7 @@ class FastEtherCat(SimpleEtherCat):
                 if ((counts[i] ^ lastcounts[i]) & 0xffff == 0
                         or (counts[i] >> 24) > 3):
                     self.send_packet(sg.assembled)
+                    print("sent", i)
                 lastcounts[i] = counts[i]
             await sleep(0.001)
 
@@ -392,6 +396,11 @@ class FastSyncGroup(SyncGroupBase, XDP):
                 p.pB[pos + Packet.ETHERNET_HEADER] = cmd.value
             for dev in self.devices:
                 dev.program()
+        for o in self.opcodes:
+            if o is not None:
+                print(o, hex(o.opcode.value))
+            else:
+                print("JMP")
         self.exit(XDPExitCode.TX)
 
     def start(self):
diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py
index 6677da0ec405e4951b62cbbeca2295778b90c273..261ee20cb9b9c3ec84728a821d616376c1566b9b 100644
--- a/ebpfcat/ethercat.py
+++ b/ebpfcat/ethercat.py
@@ -400,6 +400,8 @@ class Terminal:
             elif mode == 6:
                 self.mbx_out_off = offset
                 self.mbx_out_sz = size
+        s = await self.read(0x800, data=0x80)
+        print(absolute, " ".join(f"{c:02x} {'|' if i % 8 == 7 else ''}" for i, c in enumerate(s)))
 
     def parse_pdos(self):
         def parse_pdo(s):
@@ -602,7 +604,7 @@ class Terminal:
                         index, subindex, data)
                 type, data = await self.mbx_recv()
             if type is not MBXType.COE:
-                raise RuntimeError(f"expected CoE, got {type}")
+                raise RuntimeError(f"expected CoE, got {type}, {data} {odata} {index:x} {subindex}")
             coecmd, sdocmd, idx, subidx = unpack("<HBHB", data[:6])
             if idx != index or subindex != subidx:
                 raise RuntimeError(f"requested index {index}, got {idx}")
@@ -679,9 +681,9 @@ class Terminal:
                 if dataType == 0:
                     continue
                 assert i == oe.valueInfo
-                try:
+                if dataType < 2048:
                     oe.dataType = ECDataType(dataType)
-                except:
+                else:
                     oe.dataType = dataType
                 oe.name = data[8:].decode("utf8")
                 od.entries[i] = oe
@@ -689,38 +691,51 @@ class Terminal:
 
 
 async def main():
-    from .ebpfcat import install_ebpf
     from .bpf import lookup_elem
 
-    ec = await EtherCat("eth0")
-    map_fd = await install_ebpf("eth0")
-    tin = Terminal(ec)
-    tout = Terminal(ec)
-    tdigi = Terminal(ec)
+    ec = EtherCat("eth0")
+    await ec.connect()
+    #map_fd = await install_ebpf2()
+    tin = Terminal()
+    tin.ec = ec
+    tout = Terminal()
+    tout.ec = ec
+    tdigi = Terminal()
+    tdigi.ec = ec
     await gather(
-        tin.initialize(-1, 5),
-        tout.initialize(-2, 6),
+        tin.initialize(-4, 19),
+        tout.initialize(-2, 55),
         tdigi.initialize(0, 22),
         )
     print("tin")
-    await tin.to_operational(),
+    #await tin.to_operational()
+    await tin.set_state(2)
     print("tout")
-    await tout.to_operational(),
-    odlist = await tin.read_ODlist()
-    for o in odlist:
+    await tout.to_operational()
+    print("reading odlist")
+    odlist2, odlist = await gather(tin.read_ODlist(), tout.read_ODlist())
+    #oe = odlist[0x7001][1]
+    #await oe.write(1)
+    for o in odlist.values():
         print(hex(o.index), o.name, o.maxSub)
         for i, p in o.entries.items():
             print("   ", i, p.name, "|", p.dataType, p.bitLength, p.objectAccess)
             #sdo = await tin.sdo_read(o.index, i)
-            sdo = await p.read()
-            print("   ", sdo)
-    print("tdigi")
-    print("bla", lookup_elem(map_fd, b"AAAA", 4))
-    await tdigi.to_operational(),
-
-    print(tout.eeprom[10])
-    print(await tout.write(0x1100, "HHHH", 10000, 20000, 30000, 40000))
-    print(await tin.read(0x1180, "HHHHHHHH"))
+            try:
+               sdo = await p.read()
+               if isinstance(sdo, int):
+                   t = hex(sdo)
+               else:
+                   t = ""
+               print("   ", sdo, t)
+            except RuntimeError as e:
+               print("   E", e)
+    print("set sdo")
+    oe = odlist[0x8010][7]
+    print("=", await oe.read())
+    await oe.write(1)
+    print("=", await oe.read())
+    print(tdigi.eeprom[10])
 
 if __name__ == "__main__":
     loop = get_event_loop()
diff --git a/ebpfcat/ethercat_test.py b/ebpfcat/ethercat_test.py
index 9921fd565ad7749e668f7b8ec64d400c16f7bbdd..f95f0c701ae51b3d64986a7ea01fc95bac6f1dfa 100644
--- a/ebpfcat/ethercat_test.py
+++ b/ebpfcat/ethercat_test.py
@@ -18,7 +18,7 @@
 from asyncio import CancelledError, Future, get_event_loop, sleep, gather
 from unittest import TestCase, main
 
-from .devices import AnalogInput, AnalogOutput
+from .devices import AnalogInput, AnalogOutput, Motor
 from .terminals import EL4104, EL3164, EK1814
 from .ethercat import ECCmd
 from .ebpfcat import (
@@ -237,5 +237,84 @@ class Tests(TestCase):
             Instruction(opcode=O.MOV+O.LONG, dst=0, src=0, off=0, imm=3),
             Instruction(opcode=O.EXIT, dst=0, src=0, off=0, imm=0)])
 
+
+    def tet_two(self):
+        ti1 = EL3164()
+        ti2 = EL3164()
+        ti1.pdo_in_sz = ti2.pdo_in_sz = 4
+        ti1.pdo_in_off = ti2.pdo_in_off = 0xABCD
+        ti1.position = 0x77
+        ti2.position = 0x99
+        ti1.pdo_out_sz = ti2.pdo_out_sz = None
+        ti1.pdo_out_off = ti2.pdo_out_off = None
+        ec = MockEtherCat(self)
+        ti1.ec = ti2.ec = ec
+        ai1 = AnalogInput(ti1.ch1_value)
+        ai2 = AnalogInput(ti2.ch1_value)
+        ai3 = AnalogInput(ti1.ch1_attrs)
+        SyncGroup.packet_index = 1000
+        sg = SyncGroup(ec, [ai1, ai2, ai3])
+        self.task = sg.start()
+        ec.expected = [
+            (ECCmd.FPRD, 0x99, 304, "H2xH"),  # get state
+            (ECCmd.FPRD, 0x77, 304, "H2xH"),  # get state
+            bytes.fromhex("2d10"  # EtherCAT Header, length & type
+                          "0000e8030000008000000000"  # ID datagram
+                          "04007700cdab04800000000000000000" # in datagram
+                          "050077002143030000000000000000"), # out datagram
+            1000, # == 0x3e8, see ID datagram
+            bytes.fromhex("2d10"  # EtherCAT Header, length & type
+                          "0000e8030000008000000000"  # ID datagram
+                          "04007700cdab04800000123456780000" # in datagram
+                          "050077002143030000000000000000"), # out datagram
+            1000,
+            ]
+        ec.results = [
+            (8, 0),  # return state 8, no error
+            (8, 0),  # return state 8, no error
+            bytes.fromhex("2d10"  # EtherCAT Header, length & type
+                          "0000e8030000008000000000"  # ID datagram
+                          "04007700cdab04800000123456780000" # in datagram
+                          "050077002143030000000000000000"), # out datagram
+            ]
+        self.future = Future()
+        with self.assertRaises(CancelledError):
+            get_event_loop().run_until_complete(
+                    gather(self.future, self.task))
+        self.assertEqual(ai.value, 0x7856)
+        self.task.cancel()
+        with self.assertRaises(CancelledError):
+            get_event_loop().run_until_complete(self.task)
+
+    def test_motor(self):
+        class T(EBPFTerminal):
+            v = PacketDesc((0, 2), "H")
+            e = PacketDesc((1, 0), "H")
+            q = PacketDesc((0, 0), 0)
+        t = T()
+        t.pdo_in_sz = 2
+        t.pdo_in_off = 0x1234
+        t.pdo_out_sz = 4
+        t.pdo_out_off = 0x5678
+        t.position = 7
+        m = Motor()
+        m.velocity = t.v
+        m.encoder = t.e
+        m.low_switch = m.high_switch = t.q
+        me = MockEtherCat(self)
+        me.expected = [
+            bytes.fromhex("2c10"
+                          "000033000000008000000000"
+                          "0400070034120280000000000000"
+                          "05000700785604000000000000000000")]
+        sg = FastSyncGroup(me, [m])
+        sg.start()
+        #sg.program()
+        #sg.opcodes = sg.opcodes[:-1]
+        print(sg.load(log_level=1)[1])
+        self.maxDiff = None
+        self.assertEqual(sg.opcodes, [])
+
+
 if __name__ == "__main__":
     main()
diff --git a/ebpfcat/testsystem.py b/ebpfcat/testsystem.py
deleted file mode 100644
index be7aab353c242c9490ca4e78112f6bf93f633da4..0000000000000000000000000000000000000000
--- a/ebpfcat/testsystem.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# ebpfcat, A Python-based EBPF generator and EtherCAT master
-# Copyright (C) 2021 Martin Teichmann <martin.teichmann@gmail.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-from asyncio import gather, sleep, ensure_future
-from .terminals import EL3164, EL4104, EK1814
-from .devices import AnalogInput, AnalogOutput, DigitalInput, DigitalOutput
-from .ebpfcat import FastEtherCat, FastSyncGroup, SyncGroup
-
-tdigi = EK1814()
-tout = EL4104()
-tin = EL3164()
-
-ec = FastEtherCat("eth0", [tdigi, tin, tout])
-
-
-async def monitor(ec):
-    while True:
-        print("M", ec.ebpf.count, ec.ebpf.allcount, await tin.get_state())
-        await sleep(0.1)
-
-
-async def main():
-    await ec.connect()
-    await ec.scan_bus()
-    #ensure_future(monitor(ec))
-
-    ai = AnalogInput(tin.ch1_value)
-    ao = AnalogOutput(tout.ch1_value)
-    di = DigitalInput(tdigi.ch1)
-    do = DigitalOutput(tdigi.ch8)
-    #fsg = FastSyncGroup(ec, [ai, ao])
-    fsg = SyncGroup(ec, [ai, ao, do, di])
-
-    fsg.start()
-    ao.value = 0
-    do.value = False
-
-    for i in range(100):
-        await sleep(0.1)
-        #fsg.properties.read()
-        ao.value = 300 * i
-        do.value = (i % 7) in (0, 1, 2, 5)
-        #fsg.properties.write()
-        print(i, ai.value, ao.value, di.value, await tout.get_state())
-
-if __name__ == "__main__":
-    from asyncio import get_event_loop
-    loop = get_event_loop()
-    loop.run_until_complete(main())
diff --git a/setup.cfg b/setup.cfg
index 97986f0de36f3446b8586290dd35c58a68af2c5e..a77669319d761a69002241d5e251c069daffc991 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = ebpfcat
-version = 0.0.1
+version = 0.1.0
 author = Martin Teichmann
 author_email = martin.teichmann@xfel.eu
 description = An EtherCAT master written in Python using EBPF and XDP