]> sigrok.org Git - libsigrokdecode.git/blob - decoders/z80/pd.py
z80: New decoder for disassembling Z80 CPU instructions.
[libsigrokdecode.git] / decoders / z80 / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 3 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
18 ##
19
20 import sigrokdecode as srd
21 from functools import reduce
22 from .tables import instr_table_by_prefix
23
24 class Ann:
25     ADDR, MEMRD, MEMWR, IORD, IOWR, INSTR, ROP, WOP, WARN = range(9)
26 class Row:
27     ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5)
28 class Pin:
29     D0, D7 = 0, 7
30     M1, RD, WR, MREQ, IORQ = range(8, 13)
31     A0, A15 = 13, 28
32 class Cycle:
33     NONE, MEMRD, MEMWR, IORD, IOWR, FETCH, INTACK = range(7)
34
35 class OpState:
36     IDLE    = 'IDLE'    # no current instruction
37     PRE1    = 'PRE1'    # first prefix
38     PRE2    = 'PRE2'    # second prefix
39     PREDIS  = 'PREDIS'  # pre-opcode displacement
40     OPCODE  = 'OPCODE'  # opcode byte
41     POSTDIS = 'POSTDIS' # post-opcode displacement
42     IMM1    = 'IMM1'    # first byte of immediate
43     IMM2    = 'IMM2'    # second byte of immediate
44     ROP1    = 'ROP1'    # first byte of read operand
45     ROP2    = 'ROP2'    # second byte of read operand
46     WOP1    = 'WOP1'    # first byte of write operand
47     WOP2    = 'WOP2'    # second byte of write operand
48     RESTART = 'RESTART' # restart instruction decoding
49
50 ann_data_cycle_map = {
51     Cycle.MEMRD:  Ann.MEMRD,
52     Cycle.MEMWR:  Ann.MEMWR,
53     Cycle.IORD:   Ann.IORD,
54     Cycle.IOWR:   Ann.IOWR,
55     Cycle.FETCH:  Ann.MEMRD,
56     Cycle.INTACK: Ann.IORD,
57 }
58
59 def reduce_bus(bus):
60     if 0xFF in bus:
61         return None # unassigned bus probes
62     else:
63         return reduce(lambda a, b: (a << 1) | b, reversed(bus))
64
65 def signed_byte(byte):
66     return byte if byte < 128 else byte - 256
67
68 class Decoder(srd.Decoder):
69     api_version = 1
70     id          = 'z80'
71     name        = 'Z80'
72     longname    = 'Zilog Z80 CPU'
73     desc        = 'Zilog Z80 microprocessor disassembly.'
74     license     = 'gplv2+'
75     inputs      = ['logic']
76     outputs     = ['z80']
77     probes = [
78         {'id': 'd0', 'name': 'D0',  'desc': 'Data bus line 0'},
79         {'id': 'd1', 'name': 'D1',  'desc': 'Data bus line 1'},
80         {'id': 'd2', 'name': 'D2',  'desc': 'Data bus line 2'},
81         {'id': 'd3', 'name': 'D3',  'desc': 'Data bus line 3'},
82         {'id': 'd4', 'name': 'D4',  'desc': 'Data bus line 4'},
83         {'id': 'd5', 'name': 'D5',  'desc': 'Data bus line 5'},
84         {'id': 'd6', 'name': 'D6',  'desc': 'Data bus line 6'},
85         {'id': 'd7', 'name': 'D7',  'desc': 'Data bus line 7'},
86         {'id': 'm1', 'name': '/M1', 'desc': 'Machine cycle 1'},
87         {'id': 'rd', 'name': '/RD', 'desc': 'Memory or I/O read'},
88         {'id': 'wr', 'name': '/WR', 'desc': 'Memory or I/O write'},
89     ]
90     optional_probes = [
91         {'id': 'mreq', 'name': '/MREQ', 'desc': 'Memory request'},
92         {'id': 'iorq', 'name': '/IORQ', 'desc': 'I/O request'},
93         {'id': 'a0',   'name': 'A0',    'desc': 'Address bus line 0'},
94         {'id': 'a1',   'name': 'A1',    'desc': 'Address bus line 1'},
95         {'id': 'a2',   'name': 'A2',    'desc': 'Address bus line 2'},
96         {'id': 'a3',   'name': 'A3',    'desc': 'Address bus line 3'},
97         {'id': 'a4',   'name': 'A4',    'desc': 'Address bus line 4'},
98         {'id': 'a5',   'name': 'A5',    'desc': 'Address bus line 5'},
99         {'id': 'a6',   'name': 'A6',    'desc': 'Address bus line 6'},
100         {'id': 'a7',   'name': 'A7',    'desc': 'Address bus line 7'},
101         {'id': 'a8',   'name': 'A8',    'desc': 'Address bus line 8'},
102         {'id': 'a9',   'name': 'A9',    'desc': 'Address bus line 9'},
103         {'id': 'a10',  'name': 'A10',   'desc': 'Address bus line 10'},
104         {'id': 'a11',  'name': 'A11',   'desc': 'Address bus line 11'},
105         {'id': 'a12',  'name': 'A12',   'desc': 'Address bus line 12'},
106         {'id': 'a13',  'name': 'A13',   'desc': 'Address bus line 13'},
107         {'id': 'a14',  'name': 'A14',   'desc': 'Address bus line 14'},
108         {'id': 'a15',  'name': 'A15',   'desc': 'Address bus line 15'},
109     ]
110     options = {}
111     annotations = [
112         ['addr',    'Memory or I/O address'],
113         ['memrd',   'Byte read from memory'],
114         ['memwr',   'Byte written to memory'],
115         ['iord',    'Byte read from I/O port'],
116         ['iowr',    'Byte written to I/O port'],
117         ['instr',   'Z80 CPU instruction'],
118         ['rop',     'Value of input operand'],
119         ['wop',     'Value of output operand'],
120         ['warning', 'Warning message'],
121     ]
122     annotation_rows = (
123         ('addrbus', 'Address bus', (Ann.ADDR,)),
124         ('databus', 'Data bus', (Ann.MEMRD, Ann.MEMWR, Ann.IORD, Ann.IOWR)),
125         ('instructions', 'Instructions', (Ann.INSTR,)),
126         ('operands', 'Operands', (Ann.ROP, Ann.WOP)),
127         ('warnings', 'Warnings', (Ann.WARN,))
128     )
129
130     def __init__(self, **kwargs):
131         self.prev_cycle = Cycle.NONE
132         self.op_state   = OpState.IDLE
133
134     def start(self):
135         self.out_ann    = self.register(srd.OUTPUT_ANN)
136         self.bus_data   = None
137         self.samplenum  = None
138         self.addr_start = None
139         self.data_start = None
140         self.dasm_start = None
141         self.pend_addr  = None
142         self.pend_data  = None
143         self.ann_data   = None
144         self.ann_dasm   = None
145         self.prev_cycle = Cycle.NONE
146         self.op_state   = OpState.IDLE
147
148     def decode(self, ss, es, data):
149         for (self.samplenum, pins) in data:
150             cycle = Cycle.NONE
151             if pins[Pin.MREQ] != 1: # default to asserted
152                 if pins[Pin.RD] == 0:
153                     cycle = Cycle.FETCH if pins[Pin.M1] == 0 else Cycle.MEMRD
154                 elif pins[Pin.WR] == 0:
155                     cycle = Cycle.MEMWR
156             elif pins[Pin.IORQ] == 0: # default to not asserted
157                 if pins[Pin.M1] == 0:
158                     cycle = Cycle.INTACK
159                 elif pins[Pin.RD] == 0:
160                     cycle = Cycle.IORD
161                 elif pins[Pin.WR] == 0:
162                     cycle = Cycle.IOWR
163
164             if cycle != Cycle.NONE:
165                 self.bus_data = reduce_bus(pins[Pin.D0:Pin.D7+1])
166             if cycle != self.prev_cycle:
167                 if self.prev_cycle == Cycle.NONE:
168                     self.on_cycle_begin(reduce_bus(pins[Pin.A0:Pin.A15+1]))
169                 elif cycle == Cycle.NONE:
170                     self.on_cycle_end()
171                 else:
172                     self.on_cycle_trans()
173             self.prev_cycle = cycle
174
175     def on_cycle_begin(self, bus_addr):
176         if self.pend_addr is not None:
177             self.put_text(self.addr_start, Ann.ADDR,
178                           '{:04X}'.format(self.pend_addr))
179         self.addr_start = self.samplenum
180         self.pend_addr  = bus_addr
181
182     def on_cycle_end(self):
183         self.op_state = getattr(self, 'on_state_' + self.op_state)()
184         if self.ann_dasm is not None:
185             self.put_disasm()
186         if self.op_state == OpState.RESTART:
187             self.op_state = self.on_state_IDLE()
188
189         if self.ann_data is not None:
190             self.put_text(self.data_start, self.ann_data,
191                           '{:02X}'.format(self.pend_data))
192         self.data_start = self.samplenum
193         self.pend_data  = self.bus_data
194         self.ann_data   = ann_data_cycle_map[self.prev_cycle]
195
196     def on_cycle_trans(self):
197         self.put_text(self.samplenum - 1, Ann.WARN,
198                       'Illegal transition between control states')
199         self.pend_addr = None
200         self.ann_data  = None
201         self.ann_dasm  = None
202
203     def put_disasm(self):
204         text = self.mnemonic.format(r=self.arg_reg, d=self.arg_dis,
205                                     i=self.arg_imm, ro=self.arg_read,
206                                     wo=self.arg_write)
207         self.put_text(self.dasm_start, self.ann_dasm, text)
208         self.ann_dasm   = None
209         self.dasm_start = self.samplenum
210
211     def put_text(self, ss, ann_idx, ann_text):
212         self.put(ss, self.samplenum, self.out_ann, [ann_idx, [ann_text]])
213
214     def on_state_IDLE(self):
215         if self.prev_cycle != Cycle.FETCH:
216             return OpState.IDLE
217         self.want_dis   = 0
218         self.want_imm   = 0
219         self.want_read  = 0
220         self.want_write = 0
221         self.want_wr_be = False
222         self.op_repeat  = False
223         self.arg_dis    = 0
224         self.arg_imm    = 0
225         self.arg_read   = 0
226         self.arg_write  = 0
227         self.arg_reg    = ''
228         self.mnemonic   = ''
229         self.instr_pend = False
230         self.read_pend  = False
231         self.write_pend = False
232         self.dasm_start = self.samplenum
233         self.op_prefix  = 0
234         if self.bus_data in (0xCB, 0xED, 0xDD, 0xFD):
235             return OpState.PRE1
236         else:
237             return OpState.OPCODE
238
239     def on_state_PRE1(self):
240         if self.prev_cycle != Cycle.FETCH:
241             self.mnemonic = 'Prefix not followed by fetch'
242             self.ann_dasm = Ann.WARN
243             return OpState.RESTART
244         self.op_prefix = self.pend_data
245         if self.op_prefix in (0xDD, 0xFD):
246             if self.bus_data == 0xCB:
247                 return OpState.PRE2
248             if self.bus_data in (0xDD, 0xED, 0xFD):
249                 return OpState.PRE1
250         return OpState.OPCODE
251
252     def on_state_PRE2(self):
253         if self.prev_cycle != Cycle.MEMRD:
254             self.mnemonic = 'Missing displacement'
255             self.ann_dasm = Ann.WARN
256             return OpState.RESTART
257         self.op_prefix = (self.op_prefix << 8) | self.pend_data
258         return OpState.PREDIS
259
260     def on_state_PREDIS(self):
261         if self.prev_cycle != Cycle.MEMRD:
262             self.mnemonic = 'Missing opcode'
263             self.ann_dasm = Ann.WARN
264             return OpState.RESTART
265         self.arg_dis = signed_byte(self.pend_data)
266         return OpState.OPCODE
267
268     def on_state_OPCODE(self):
269         (table, self.arg_reg) = instr_table_by_prefix[self.op_prefix]
270         self.op_prefix = 0
271         instruction = table.get(self.pend_data, None)
272         if instruction is None:
273             self.mnemonic = 'Invalid instruction'
274             self.ann_dasm = Ann.WARN
275             return OpState.RESTART
276         (self.want_dis, self.want_imm, self.want_read, want_write,
277                 self.op_repeat, self.mnemonic) = instruction
278         self.want_write = abs(want_write)
279         self.want_wr_be = (want_write < 0)
280         if self.want_dis > 0:
281             return OpState.POSTDIS
282         if self.want_imm > 0:
283             return OpState.IMM1
284         self.ann_dasm = Ann.INSTR
285         if self.want_read > 0 and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
286             return OpState.ROP1
287         if self.want_write > 0 and self.prev_cycle in (Cycle.MEMWR, Cycle.IOWR):
288             return OpState.WOP1
289         return OpState.RESTART
290
291     def on_state_POSTDIS(self):
292         self.arg_dis = signed_byte(self.pend_data)
293         if self.want_imm > 0:
294             return OpState.IMM1
295         self.ann_dasm = Ann.INSTR
296         if self.want_read > 0:
297             return OpState.ROP1
298         if self.want_write > 0:
299             return OpState.WOP1
300         return OpState.RESTART
301
302     def on_state_IMM1(self):
303         self.arg_imm = self.pend_data
304         if self.want_imm > 1:
305             return OpState.IMM2
306         self.ann_dasm = Ann.INSTR
307         if self.want_read > 0:
308             return OpState.ROP1
309         if self.want_write > 0:
310             return OpState.WOP1
311         return OpState.RESTART
312
313     def on_state_IMM2(self):
314         self.arg_imm |= self.pend_data << 8
315         self.ann_dasm = Ann.INSTR
316         if self.want_read > 0:
317             return OpState.ROP1
318         if self.want_write > 0:
319             return OpState.WOP1
320         return OpState.RESTART
321
322     def on_state_ROP1(self):
323         self.arg_read = self.pend_data
324         if self.want_write > 0:
325             return OpState.WOP1
326         if self.want_read > 1:
327             return OpState.ROP2
328         if self.op_repeat and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
329             return OpState.ROP1
330         self.mnemonic = '{ro:02X}'
331         self.ann_dasm = Ann.ROP
332         return OpState.RESTART
333
334     def on_state_ROP2(self):
335         self.arg_read |= self.pend_data << 8
336         self.mnemonic = '{ro:04X}'
337         self.ann_dasm = Ann.ROP
338         if self.want_write > 0:
339             return OpState.WOP1
340         return OpState.RESTART
341
342     def on_state_WOP1(self):
343         self.arg_write = self.pend_data
344         if self.want_read > 1:
345             return OpState.ROP2
346         if self.want_write > 1:
347             return OpState.WOP2
348         if self.want_read > 0 and self.op_repeat and \
349                 self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
350             return OpState.ROP1
351         self.mnemonic = '{wo:02X}'
352         self.ann_dasm = Ann.WOP
353         return OpState.RESTART
354
355     def on_state_WOP2(self):
356         if self.want_wr_be:
357             self.arg_write = (self.arg_write << 8) | self.pend_data
358         else:
359             self.arg_write |= self.pend_data << 8
360         self.mnemonic = '{wo:04X}'
361         self.ann_dasm = Ann.WOP
362         return OpState.RESTART