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