]> sigrok.org Git - libsigrokdecode.git/blame - decoders/z80/pd.py
z80: New decoder for disassembling Z80 CPU instructions.
[libsigrokdecode.git] / decoders / z80 / pd.py
CommitLineData
26abbf37
DE
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
20import sigrokdecode as srd
21from functools import reduce
22from .tables import instr_table_by_prefix
23
24class Ann:
25 ADDR, MEMRD, MEMWR, IORD, IOWR, INSTR, ROP, WOP, WARN = range(9)
26class Row:
27 ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5)
28class Pin:
29 D0, D7 = 0, 7
30 M1, RD, WR, MREQ, IORQ = range(8, 13)
31 A0, A15 = 13, 28
32class Cycle:
33 NONE, MEMRD, MEMWR, IORD, IOWR, FETCH, INTACK = range(7)
34
35class 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
50ann_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
59def 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
65def signed_byte(byte):
66 return byte if byte < 128 else byte - 256
67
68class 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