diff options
Diffstat (limited to 'decoders/z80/pd.py')
-rw-r--r-- | decoders/z80/pd.py | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/decoders/z80/pd.py b/decoders/z80/pd.py new file mode 100644 index 0000000..2baa02a --- /dev/null +++ b/decoders/z80/pd.py @@ -0,0 +1,362 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2014 Daniel Elstner <daniel.kitta@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 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 <http://www.gnu.org/licenses/>. +## + +import sigrokdecode as srd +from functools import reduce +from .tables import instr_table_by_prefix + +class Ann: + ADDR, MEMRD, MEMWR, IORD, IOWR, INSTR, ROP, WOP, WARN = range(9) +class Row: + ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5) +class Pin: + D0, D7 = 0, 7 + M1, RD, WR, MREQ, IORQ = range(8, 13) + A0, A15 = 13, 28 +class Cycle: + NONE, MEMRD, MEMWR, IORD, IOWR, FETCH, INTACK = range(7) + +class OpState: + IDLE = 'IDLE' # no current instruction + PRE1 = 'PRE1' # first prefix + PRE2 = 'PRE2' # second prefix + PREDIS = 'PREDIS' # pre-opcode displacement + OPCODE = 'OPCODE' # opcode byte + POSTDIS = 'POSTDIS' # post-opcode displacement + IMM1 = 'IMM1' # first byte of immediate + IMM2 = 'IMM2' # second byte of immediate + ROP1 = 'ROP1' # first byte of read operand + ROP2 = 'ROP2' # second byte of read operand + WOP1 = 'WOP1' # first byte of write operand + WOP2 = 'WOP2' # second byte of write operand + RESTART = 'RESTART' # restart instruction decoding + +ann_data_cycle_map = { + Cycle.MEMRD: Ann.MEMRD, + Cycle.MEMWR: Ann.MEMWR, + Cycle.IORD: Ann.IORD, + Cycle.IOWR: Ann.IOWR, + Cycle.FETCH: Ann.MEMRD, + Cycle.INTACK: Ann.IORD, +} + +def reduce_bus(bus): + if 0xFF in bus: + return None # unassigned bus probes + else: + return reduce(lambda a, b: (a << 1) | b, reversed(bus)) + +def signed_byte(byte): + return byte if byte < 128 else byte - 256 + +class Decoder(srd.Decoder): + api_version = 1 + id = 'z80' + name = 'Z80' + longname = 'Zilog Z80 CPU' + desc = 'Zilog Z80 microprocessor disassembly.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['z80'] + probes = [ + {'id': 'd0', 'name': 'D0', 'desc': 'Data bus line 0'}, + {'id': 'd1', 'name': 'D1', 'desc': 'Data bus line 1'}, + {'id': 'd2', 'name': 'D2', 'desc': 'Data bus line 2'}, + {'id': 'd3', 'name': 'D3', 'desc': 'Data bus line 3'}, + {'id': 'd4', 'name': 'D4', 'desc': 'Data bus line 4'}, + {'id': 'd5', 'name': 'D5', 'desc': 'Data bus line 5'}, + {'id': 'd6', 'name': 'D6', 'desc': 'Data bus line 6'}, + {'id': 'd7', 'name': 'D7', 'desc': 'Data bus line 7'}, + {'id': 'm1', 'name': '/M1', 'desc': 'Machine cycle 1'}, + {'id': 'rd', 'name': '/RD', 'desc': 'Memory or I/O read'}, + {'id': 'wr', 'name': '/WR', 'desc': 'Memory or I/O write'}, + ] + optional_probes = [ + {'id': 'mreq', 'name': '/MREQ', 'desc': 'Memory request'}, + {'id': 'iorq', 'name': '/IORQ', 'desc': 'I/O request'}, + {'id': 'a0', 'name': 'A0', 'desc': 'Address bus line 0'}, + {'id': 'a1', 'name': 'A1', 'desc': 'Address bus line 1'}, + {'id': 'a2', 'name': 'A2', 'desc': 'Address bus line 2'}, + {'id': 'a3', 'name': 'A3', 'desc': 'Address bus line 3'}, + {'id': 'a4', 'name': 'A4', 'desc': 'Address bus line 4'}, + {'id': 'a5', 'name': 'A5', 'desc': 'Address bus line 5'}, + {'id': 'a6', 'name': 'A6', 'desc': 'Address bus line 6'}, + {'id': 'a7', 'name': 'A7', 'desc': 'Address bus line 7'}, + {'id': 'a8', 'name': 'A8', 'desc': 'Address bus line 8'}, + {'id': 'a9', 'name': 'A9', 'desc': 'Address bus line 9'}, + {'id': 'a10', 'name': 'A10', 'desc': 'Address bus line 10'}, + {'id': 'a11', 'name': 'A11', 'desc': 'Address bus line 11'}, + {'id': 'a12', 'name': 'A12', 'desc': 'Address bus line 12'}, + {'id': 'a13', 'name': 'A13', 'desc': 'Address bus line 13'}, + {'id': 'a14', 'name': 'A14', 'desc': 'Address bus line 14'}, + {'id': 'a15', 'name': 'A15', 'desc': 'Address bus line 15'}, + ] + options = {} + annotations = [ + ['addr', 'Memory or I/O address'], + ['memrd', 'Byte read from memory'], + ['memwr', 'Byte written to memory'], + ['iord', 'Byte read from I/O port'], + ['iowr', 'Byte written to I/O port'], + ['instr', 'Z80 CPU instruction'], + ['rop', 'Value of input operand'], + ['wop', 'Value of output operand'], + ['warning', 'Warning message'], + ] + annotation_rows = ( + ('addrbus', 'Address bus', (Ann.ADDR,)), + ('databus', 'Data bus', (Ann.MEMRD, Ann.MEMWR, Ann.IORD, Ann.IOWR)), + ('instructions', 'Instructions', (Ann.INSTR,)), + ('operands', 'Operands', (Ann.ROP, Ann.WOP)), + ('warnings', 'Warnings', (Ann.WARN,)) + ) + + def __init__(self, **kwargs): + self.prev_cycle = Cycle.NONE + self.op_state = OpState.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 = OpState.IDLE + + def decode(self, ss, es, data): + for (self.samplenum, pins) in data: + cycle = Cycle.NONE + if pins[Pin.MREQ] != 1: # default to asserted + if pins[Pin.RD] == 0: + cycle = Cycle.FETCH if pins[Pin.M1] == 0 else Cycle.MEMRD + elif pins[Pin.WR] == 0: + cycle = Cycle.MEMWR + elif pins[Pin.IORQ] == 0: # default to not asserted + if pins[Pin.M1] == 0: + cycle = Cycle.INTACK + elif pins[Pin.RD] == 0: + cycle = Cycle.IORD + elif pins[Pin.WR] == 0: + cycle = Cycle.IOWR + + 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 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.op_state = getattr(self, 'on_state_' + self.op_state)() + if self.ann_dasm is not None: + self.put_disasm() + if self.op_state == OpState.RESTART: + self.op_state = self.on_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 put_disasm(self): + text = self.mnemonic.format(r=self.arg_reg, d=self.arg_dis, + 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 + + def put_text(self, ss, ann_idx, ann_text): + self.put(ss, self.samplenum, self.out_ann, [ann_idx, [ann_text]]) + + def on_state_IDLE(self): + if self.prev_cycle != Cycle.FETCH: + return OpState.IDLE + 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 + if self.bus_data in (0xCB, 0xED, 0xDD, 0xFD): + return OpState.PRE1 + else: + return OpState.OPCODE + + def on_state_PRE1(self): + if self.prev_cycle != Cycle.FETCH: + self.mnemonic = 'Prefix not followed by fetch' + self.ann_dasm = Ann.WARN + return OpState.RESTART + self.op_prefix = self.pend_data + if self.op_prefix in (0xDD, 0xFD): + if self.bus_data == 0xCB: + return OpState.PRE2 + if self.bus_data in (0xDD, 0xED, 0xFD): + return OpState.PRE1 + return OpState.OPCODE + + def on_state_PRE2(self): + if self.prev_cycle != Cycle.MEMRD: + self.mnemonic = 'Missing displacement' + self.ann_dasm = Ann.WARN + return OpState.RESTART + self.op_prefix = (self.op_prefix << 8) | self.pend_data + return OpState.PREDIS + + def on_state_PREDIS(self): + if self.prev_cycle != Cycle.MEMRD: + self.mnemonic = 'Missing opcode' + self.ann_dasm = Ann.WARN + return OpState.RESTART + self.arg_dis = signed_byte(self.pend_data) + return OpState.OPCODE + + def on_state_OPCODE(self): + (table, self.arg_reg) = instr_table_by_prefix[self.op_prefix] + self.op_prefix = 0 + instruction = table.get(self.pend_data, None) + if instruction is None: + self.mnemonic = 'Invalid instruction' + self.ann_dasm = Ann.WARN + return OpState.RESTART + (self.want_dis, self.want_imm, self.want_read, want_write, + self.op_repeat, self.mnemonic) = instruction + self.want_write = abs(want_write) + self.want_wr_be = (want_write < 0) + if self.want_dis > 0: + return OpState.POSTDIS + if self.want_imm > 0: + return OpState.IMM1 + self.ann_dasm = Ann.INSTR + if self.want_read > 0 and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD): + return OpState.ROP1 + if self.want_write > 0 and self.prev_cycle in (Cycle.MEMWR, Cycle.IOWR): + return OpState.WOP1 + return OpState.RESTART + + def on_state_POSTDIS(self): + self.arg_dis = signed_byte(self.pend_data) + if self.want_imm > 0: + return OpState.IMM1 + self.ann_dasm = Ann.INSTR + if self.want_read > 0: + return OpState.ROP1 + if self.want_write > 0: + return OpState.WOP1 + return OpState.RESTART + + def on_state_IMM1(self): + self.arg_imm = self.pend_data + if self.want_imm > 1: + return OpState.IMM2 + self.ann_dasm = Ann.INSTR + if self.want_read > 0: + return OpState.ROP1 + if self.want_write > 0: + return OpState.WOP1 + return OpState.RESTART + + def on_state_IMM2(self): + self.arg_imm |= self.pend_data << 8 + self.ann_dasm = Ann.INSTR + if self.want_read > 0: + return OpState.ROP1 + if self.want_write > 0: + return OpState.WOP1 + return OpState.RESTART + + def on_state_ROP1(self): + self.arg_read = self.pend_data + if self.want_write > 0: + return OpState.WOP1 + if self.want_read > 1: + return OpState.ROP2 + if self.op_repeat and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD): + return OpState.ROP1 + self.mnemonic = '{ro:02X}' + self.ann_dasm = Ann.ROP + return OpState.RESTART + + def on_state_ROP2(self): + self.arg_read |= self.pend_data << 8 + self.mnemonic = '{ro:04X}' + self.ann_dasm = Ann.ROP + if self.want_write > 0: + return OpState.WOP1 + return OpState.RESTART + + def on_state_WOP1(self): + self.arg_write = self.pend_data + if self.want_read > 1: + return OpState.ROP2 + if self.want_write > 1: + return OpState.WOP2 + if self.want_read > 0 and self.op_repeat and \ + self.prev_cycle in (Cycle.MEMRD, Cycle.IORD): + return OpState.ROP1 + self.mnemonic = '{wo:02X}' + self.ann_dasm = Ann.WOP + return OpState.RESTART + + def on_state_WOP2(self): + if self.want_wr_be: + self.arg_write = (self.arg_write << 8) | self.pend_data + else: + self.arg_write |= self.pend_data << 8 + self.mnemonic = '{wo:04X}' + self.ann_dasm = Ann.WOP + return OpState.RESTART |