diff --git a/decoders/mc6809/__init__.py b/decoders/mc6809/__init__.py
new file mode 100755
index 00000000..37df61bf
--- /dev/null
+++ b/decoders/mc6809/__init__.py
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2026, fjkraan@electrickery.nl
+##
+## 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 3 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, see .
+##
+
+'''
+The Motorola MC6809 is an 8-bit microprocessor.
+
+In addition to the 8-bit data bus, this decoder requires the input signals
+E, Q, RD/WR (read-not-write) to do its work. An explicit
+clock signal is not required.
+
+Details:
+https://ia803103.us.archive.org/14/items/mc6809mc6809e8bitmicroprocessorprogrammingmanualmotorolainc.1981/MC6809-MC6809E%208-Bit%20Microprocessor%20Programming%20Manual%20%28Motorola%20Inc.%29%201981.pdf
+https://github.com/M6809-Docs/m6809pm
+'''
+
+from .pd import Decoder
diff --git a/decoders/mc6809/pd.py b/decoders/mc6809/pd.py
new file mode 100644
index 00000000..85122db3
--- /dev/null
+++ b/decoders/mc6809/pd.py
@@ -0,0 +1,209 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2026, fjkraan@electrickery.nl
+##
+## 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, see .
+##
+## Version 1.2, 2026-01-17
+
+import sigrokdecode as srd
+from functools import reduce
+
+class Ann:
+ ADDR, MEMRD, MEMWR, INSTR, ROP, WOP, WARN = range(7) # 0, 1, 2, 3, 4, 5, 6
+
+class Row:
+ ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5) # 0, 1, 2, 3, 4
+
+class Cycle:
+ NONE, MEMRD, MEMWR = range(3)
+
+ann_data_cycle_map = {
+ Cycle.MEMRD: Ann.MEMRD,
+ Cycle.MEMWR: Ann.MEMWR,
+}
+
+class Pin:
+ RW, E, Q = range(0, 3) # 0, 1, 2
+ D0, D7 = 3, 10 # 3, 4, 5, 6, 7, 8, 9, 10
+ A0, A15 = 11, 27 # 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
+
+def reduce_bus(bus):
+ if 0xFF in bus:
+ return None # unassigned bus channels
+ else:
+ return reduce(lambda a, b: (a << 1) | b, reversed(bus))
+
+class ChannelError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'mc6809'
+ name = 'MC6809'
+ longname = 'Motorola MC6809'
+ desc = 'Decoder for the Motorola MC6809 microprocessor can be loaded by sigrok.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Retro computing']
+ channels = (
+ {'id': 'rw', 'name': 'R/W', 'desc': 'Read / not Write'}, # 0
+ {'id': 'e', 'name': 'E', 'desc': 'Bus timing / enable clock'}, # 1
+ {'id': 'q', 'name': 'Q', 'desc': 'Bus timing / quadrature clock'}, # 2
+ ) + tuple({
+ 'id': 'd%d' % i, # 3-10
+ 'name': 'D%d' % i,
+ 'desc': 'CPU data line %d' % i
+ } for i in range(0, 8)
+ ) + tuple({
+ 'id': 'a%d' % i, # 11-26
+ 'name': 'A%d' % i,
+ 'desc': 'CPU address line %d' % i
+ } for i in range(0, 16)
+ )
+ optional_channels = (
+ {'id': 'ba', 'name': 'BA', 'desc': 'Bus enable'},
+ {'id': 'bs', 'name': 'BS', 'desc': 'Bus status'},
+ ) + (
+ {'id': 'nmi', 'name': '/NMI', 'desc': 'not Non-Maskable Interrupt'},
+ {'id': 'irq', 'name': '/IRQ', 'desc': 'not Interrupt'},
+ {'id': 'firq', 'name': '/FIRQ', 'desc': 'not Fast Interrupt'},
+ {'id': 'dmabrq', 'name': '/DMA/BREQ', 'desc': 'not Direct Memory Access / not Bus Request'},
+ {'id': 'mrdy', 'name': 'MRDY', 'desc': 'Memory Ready'},
+ {'id': 'rst', 'name': '/RESET', 'desc': 'not Reset'},
+ {'id': 'halt', 'name': '/HALT', 'desc': 'not Halt'},
+ )
+ annotations = (
+ ('addr', 'Memory address'), #
+ ('memrd', 'Byte read from memory'), #
+ ('memwr', 'Byte written to memory'), #
+ ('instr', '6809 CPU instruction'), #
+ ('rop', 'Value of input operand'), #
+ ('wop', 'Value of output operand'), #
+ ('warning', 'Warning'), #
+ )
+ annotation_rows = (
+ ('addrbus', 'Addr.', (Ann.ADDR,)), # 0
+ ('databus', 'Data', (Ann.MEMRD, Ann.MEMWR)), # 1
+ ('instructions', 'Instructions', (Ann.INSTR,)), # 2
+ ('operands', 'Operands', (Ann.ROP, Ann.WOP)), # 3
+ ('warnings', 'Warnings', (Ann.WARN,)) # 4
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.prev_cycle = Cycle.NONE
+ self.op_state = self.state_IDLE
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.bus_data = None
+ self.samplenum = None
+ self.addr_start = None
+ self.data_start = None
+ self.dasm_start = None
+ self.pend_addr = None
+ self.pend_data = None
+ self.ann_data = None
+ self.ann_dasm = None
+ self.prev_cycle = Cycle.NONE
+ self.op_state = self.state_IDLE
+ self.instr_len = 0
+
+ def decode(self):
+ while True:
+ pins = self.wait()
+ cycle = Cycle.NONE
+ if pins[Pin.Q] == 1 and pins[Pin.E] == 0: # default to asserted
+ if pins[Pin.RW] == 0:
+ cycle = Cycle.MEMWR #
+ else:
+ cycle = Cycle.MEMRD #
+
+ if cycle != Cycle.NONE:
+ self.bus_data = reduce_bus(pins[Pin.D0:Pin.D7+1])
+ if cycle != self.prev_cycle:
+ if self.prev_cycle == Cycle.NONE:
+ self.on_cycle_begin(reduce_bus(pins[Pin.A0:Pin.A15+1]))
+ elif cycle == Cycle.NONE:
+ self.on_cycle_end()
+ else:
+ self.on_cycle_trans()
+ self.prev_cycle = cycle
+
+ def state_IDLE(self):
+ self.want_dis = 0
+ self.want_imm = 0
+ self.want_read = 0
+ self.want_write = 0
+ self.want_wr_be = False
+ self.op_repeat = False
+ self.arg_dis = 0
+ self.arg_imm = 0
+ self.arg_read = 0
+ self.arg_write = 0
+ self.arg_reg = ''
+ self.mnemonic = ''
+ self.instr_pend = False
+ self.read_pend = False
+ self.write_pend = False
+ self.dasm_start = self.samplenum
+ self.op_prefix = 0
+ self.instr_len = 0
+
+ def on_cycle_begin(self, bus_addr):
+ if self.pend_addr is not None:
+ self.put_text(self.addr_start, Ann.ADDR,
+ '{:04X}'.format(self.pend_addr))
+ self.addr_start = self.samplenum
+ self.pend_addr = bus_addr
+
+ def on_cycle_end(self):
+ self.instr_len += 1
+ if self.ann_dasm is not None:
+ self.put_disasm()
+ if self.op_state == self.state_RESTART:
+ self.op_state = self.state_IDLE()
+
+ if self.ann_data is not None:
+ self.put_text(self.data_start, self.ann_data,
+ '{:02X}'.format(self.pend_data))
+ self.data_start = self.samplenum
+ self.pend_data = self.bus_data
+ self.ann_data = ann_data_cycle_map[self.prev_cycle]
+
+ def on_cycle_trans(self):
+ self.put_text(self.samplenum - 1, Ann.WARN,
+ 'Illegal transition between control states')
+ self.pend_addr = None
+ self.ann_data = None
+ self.ann_dasm = None
+
+ def state_RESTART(self):
+ return self.state_IDLE
+
+ def put_text(self, ss, ann_idx, ann_text):
+ self.put(ss, self.samplenum, self.out_ann, [ann_idx, [ann_text]])
+
+ def put_disasm(self):
+ text = formatter.format(self.mnemonic, r=self.arg_reg, d=self.arg_dis,
+ j=self.arg_dis+self.instr_len, i=self.arg_imm,
+ ro=self.arg_read, wo=self.arg_write)
+ self.put_text(self.dasm_start, self.ann_dasm, text)
+ self.ann_dasm = None
+ self.dasm_start = self.samplenum
diff --git a/decoders/tms7000/__init__.py b/decoders/tms7000/__init__.py
new file mode 100644
index 00000000..5b36dcac
--- /dev/null
+++ b/decoders/tms7000/__init__.py
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2026, fjkraan@electrickery.nl
+##
+## 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 3 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, see .
+##
+
+'''
+The TMS7000 is an 8-bit microprocessor family. This decoder is for the MC-mode.
+
+In addition to the 8-bit data bus, this decoder requires the input signals
+/EN (enable), RD/WR (read not write) and AS (address strobe) to do its work. An explicit
+clock signal is not required. However, the TMS7000 CPU clock may be used as
+sampling clock, if applicable.
+
+Details:
+https://archive.org/details/bitsavers_tiTMS70001ataManual_33796916/mode/2up?q=tms7000+family+data page 4-45
+'''
+
+from .pd import Decoder
diff --git a/decoders/tms7000/pd.py b/decoders/tms7000/pd.py
new file mode 100644
index 00000000..df80d1fe
--- /dev/null
+++ b/decoders/tms7000/pd.py
@@ -0,0 +1,129 @@
+##
+## This file could become part of the libsigrokdecode project.
+##
+## Copyright (C) 2026, fjkraan@electrickery.nl
+##
+## 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, see .
+##
+## Version 1.3, 2026-01-17
+
+import sigrokdecode as srd
+
+class Ann:
+ ADDR, MEMRD, MEMWR, WARN = range(4)
+class Row:
+ ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5)
+class Pin:
+ AD0, AD7 = 0, 7 #
+ A8, A15 = 8, 15 #
+ RD_WR, ENA, ALE = 16, 17, 18 #
+
+class ChannelError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'tms7k'
+ name = 'TMS7000'
+ longname = 'Texas Instruments TMS7000'
+ desc = 'Texas Instruments TMS7000 family, MC-mode.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Retro computing']
+ channels = (
+ {'id': 'rw', 'name': 'RW', 'desc': 'Read/notWrite'}, # 0
+ {'id': 'en', 'name': 'EN', 'desc': 'Memory enable strobe'}, # 1
+ {'id': 'ale', 'name': 'ALE', 'desc': 'Read/notWrite strobe'}, # 2
+ ) + tuple({
+ 'id': 'ad%d' % i,
+ 'name': 'AD%d' % i,
+ 'desc': 'CPU data/addr. line %d' % i
+ } for i in range(0, 8) # Here the number is for formatting labels
+ ) + tuple({
+ 'id': 'a%d' % i,
+ 'name': 'A%d' % i,
+ 'desc': 'CPU address line %d' % i
+ } for i in range(8, 16) # Here the number is for formatting labels
+ )
+ optional_channels = (
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Internal clockout'},
+ {'id': 'rst', 'name': '/RESET', 'desc': 'RESET'},
+ {'id': 'int1', 'name': '/INT1', 'desc': 'Interrupt 1'},
+ {'id': 'int3', 'name': '/INT3', 'desc': 'Interrupt 3'},
+ {'id': 'mc', 'name': 'MC', 'desc': 'Microcontroller Mode'}
+ ) + tuple({
+ 'id': 'ap%d' % i,
+ 'name': 'AP%d' % i,
+ 'desc': 'CPU A port %d' % i
+ } for i in range(0, 8)
+ ) + tuple({
+ 'id': 'bp%d' % i,
+ 'name': 'BP%d' % i,
+ 'desc': 'CPU B port %d' % i
+ } for i in range(0, 4)
+ )
+ annotations = (
+ ('address', 'Address'), # 0
+ ('memrd', 'Memory Read'), # 1
+ ('memwr', 'Memory Write'), # 2
+ ('data', 'Data Bus'), # 3
+ ('addrdata', 'Address:Data'), # 4
+ )
+ annotation_rows = (
+ ('addrbus', 'Addr.', (Ann.ADDR,)),
+ ('databus', 'Data', (Ann.MEMRD, Ann.MEMWR)),
+ )
+
+ OFF_RW, OFF_EN, OFF_ALE = 0, 1, 2 # 0, 1, 2
+ OFF_DATA_BOT, OFF_DATA_TOP = 3, 10 # 3, 4, 5, 6, 7, 8, 9, 10
+ OFF_ADDR_BOT, OFF_ADDR_TOP = 11, 18 # 11, 12, 13, 14, 15, 16, 17, 18
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.addr = 0
+ self.prev_addr_samplenum = 0
+ self.data = 0
+ self.prev_data_samplenum = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_bin = self.register(srd.OUTPUT_BINARY)
+
+ def decode(self):
+ while True:
+ pins = self.wait([{self.OFF_ALE: 'f'}, {self.OFF_EN: 'r'}])
+ if self.matched[0]: # self.OFF_ALE: 'f'
+ if self.OFF_EN == 0:
+ continue
+ addrPins = pins[self.OFF_ADDR_BOT:self.OFF_ADDR_TOP + 1]
+ dataPins = pins[self.OFF_DATA_BOT:self.OFF_DATA_TOP + 1]
+ addrV = sum([bit << i for i, bit in enumerate(addrPins)])
+ dataV = sum([bit << i for i, bit in enumerate(dataPins)])
+ self.addr = addrV * 256 + dataV
+ anntext = '{:04X}'.format(self.addr)
+ self.put(self.prev_addr_samplenum, self.samplenum, self.out_ann, [Ann.ADDR, [anntext]])
+ self.prev_addr_samplenum = self.samplenum
+ if self.matched[1]: # self.OFF_EN: 'r'
+ dataPins = pins[self.OFF_DATA_BOT:self.OFF_DATA_TOP + 1]
+ dataV = sum([bit << i for i, bit in enumerate(dataPins)])
+ self.data = dataV
+ anntext = '{:02X}'.format(self.data)
+ if pins[self.OFF_RW] == 1:
+ self.put(self.prev_data_samplenum, self.samplenum, self.out_ann, [Ann.MEMRD, [anntext]])
+ else:
+ self.put(self.prev_data_samplenum, self.samplenum, self.out_ann, [Ann.MEMWR, [anntext]])
+ self.prev_data_samplenum = self.samplenum