]> sigrok.org Git - libsigrokdecode.git/blame - decoders/z80/pd.py
z80: Output jump offsets relative to instruction start.
[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
aef3c109 23import string
26abbf37
DE
24
25class Ann:
26 ADDR, MEMRD, MEMWR, IORD, IOWR, INSTR, ROP, WOP, WARN = range(9)
27class Row:
28 ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5)
29class Pin:
30 D0, D7 = 0, 7
31 M1, RD, WR, MREQ, IORQ = range(8, 13)
32 A0, A15 = 13, 28
33class Cycle:
34 NONE, MEMRD, MEMWR, IORD, IOWR, FETCH, INTACK = range(7)
35
36class OpState:
37 IDLE = 'IDLE' # no current instruction
38 PRE1 = 'PRE1' # first prefix
39 PRE2 = 'PRE2' # second prefix
40 PREDIS = 'PREDIS' # pre-opcode displacement
41 OPCODE = 'OPCODE' # opcode byte
42 POSTDIS = 'POSTDIS' # post-opcode displacement
43 IMM1 = 'IMM1' # first byte of immediate
44 IMM2 = 'IMM2' # second byte of immediate
45 ROP1 = 'ROP1' # first byte of read operand
46 ROP2 = 'ROP2' # second byte of read operand
47 WOP1 = 'WOP1' # first byte of write operand
48 WOP2 = 'WOP2' # second byte of write operand
49 RESTART = 'RESTART' # restart instruction decoding
50
aef3c109
DE
51# Provide custom format type 'H' for hexadecimal output
52# with leading decimal digit (assembler syntax).
53class AsmFormatter(string.Formatter):
54 def format_field(self, value, format_spec):
55 if format_spec.endswith('H'):
56 result = format(value, format_spec[:-1] + 'X')
57 return result if result[0] in string.digits else '0' + result
58 else:
59 return format(value, format_spec)
60
61formatter = AsmFormatter()
62
26abbf37
DE
63ann_data_cycle_map = {
64 Cycle.MEMRD: Ann.MEMRD,
65 Cycle.MEMWR: Ann.MEMWR,
66 Cycle.IORD: Ann.IORD,
67 Cycle.IOWR: Ann.IOWR,
68 Cycle.FETCH: Ann.MEMRD,
69 Cycle.INTACK: Ann.IORD,
70}
71
72def reduce_bus(bus):
73 if 0xFF in bus:
74 return None # unassigned bus probes
75 else:
76 return reduce(lambda a, b: (a << 1) | b, reversed(bus))
77
78def signed_byte(byte):
79 return byte if byte < 128 else byte - 256
80
81class Decoder(srd.Decoder):
82 api_version = 1
83 id = 'z80'
84 name = 'Z80'
85 longname = 'Zilog Z80 CPU'
86 desc = 'Zilog Z80 microprocessor disassembly.'
87 license = 'gplv2+'
88 inputs = ['logic']
89 outputs = ['z80']
90 probes = [
31737ae0
DE
91 {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data bus line %d' % i}
92 for i in range(8)
93 ] + [
26abbf37
DE
94 {'id': 'm1', 'name': '/M1', 'desc': 'Machine cycle 1'},
95 {'id': 'rd', 'name': '/RD', 'desc': 'Memory or I/O read'},
96 {'id': 'wr', 'name': '/WR', 'desc': 'Memory or I/O write'},
97 ]
98 optional_probes = [
99 {'id': 'mreq', 'name': '/MREQ', 'desc': 'Memory request'},
100 {'id': 'iorq', 'name': '/IORQ', 'desc': 'I/O request'},
31737ae0
DE
101 ] + [
102 {'id': 'a%d' % i, 'name': 'A%d' % i, 'desc': 'Address bus line %d' % i}
103 for i in range(16)
26abbf37
DE
104 ]
105 options = {}
106 annotations = [
107 ['addr', 'Memory or I/O address'],
108 ['memrd', 'Byte read from memory'],
109 ['memwr', 'Byte written to memory'],
110 ['iord', 'Byte read from I/O port'],
111 ['iowr', 'Byte written to I/O port'],
112 ['instr', 'Z80 CPU instruction'],
113 ['rop', 'Value of input operand'],
114 ['wop', 'Value of output operand'],
115 ['warning', 'Warning message'],
116 ]
117 annotation_rows = (
118 ('addrbus', 'Address bus', (Ann.ADDR,)),
119 ('databus', 'Data bus', (Ann.MEMRD, Ann.MEMWR, Ann.IORD, Ann.IOWR)),
120 ('instructions', 'Instructions', (Ann.INSTR,)),
121 ('operands', 'Operands', (Ann.ROP, Ann.WOP)),
122 ('warnings', 'Warnings', (Ann.WARN,))
123 )
124
125 def __init__(self, **kwargs):
126 self.prev_cycle = Cycle.NONE
127 self.op_state = OpState.IDLE
128
129 def start(self):
130 self.out_ann = self.register(srd.OUTPUT_ANN)
131 self.bus_data = None
132 self.samplenum = None
133 self.addr_start = None
134 self.data_start = None
135 self.dasm_start = None
136 self.pend_addr = None
137 self.pend_data = None
138 self.ann_data = None
139 self.ann_dasm = None
140 self.prev_cycle = Cycle.NONE
141 self.op_state = OpState.IDLE
8830db5d 142 self.instr_len = 0
26abbf37
DE
143
144 def decode(self, ss, es, data):
145 for (self.samplenum, pins) in data:
146 cycle = Cycle.NONE
147 if pins[Pin.MREQ] != 1: # default to asserted
148 if pins[Pin.RD] == 0:
149 cycle = Cycle.FETCH if pins[Pin.M1] == 0 else Cycle.MEMRD
150 elif pins[Pin.WR] == 0:
151 cycle = Cycle.MEMWR
152 elif pins[Pin.IORQ] == 0: # default to not asserted
153 if pins[Pin.M1] == 0:
154 cycle = Cycle.INTACK
155 elif pins[Pin.RD] == 0:
156 cycle = Cycle.IORD
157 elif pins[Pin.WR] == 0:
158 cycle = Cycle.IOWR
159
160 if cycle != Cycle.NONE:
161 self.bus_data = reduce_bus(pins[Pin.D0:Pin.D7+1])
162 if cycle != self.prev_cycle:
163 if self.prev_cycle == Cycle.NONE:
164 self.on_cycle_begin(reduce_bus(pins[Pin.A0:Pin.A15+1]))
165 elif cycle == Cycle.NONE:
166 self.on_cycle_end()
167 else:
168 self.on_cycle_trans()
169 self.prev_cycle = cycle
170
171 def on_cycle_begin(self, bus_addr):
172 if self.pend_addr is not None:
173 self.put_text(self.addr_start, Ann.ADDR,
174 '{:04X}'.format(self.pend_addr))
175 self.addr_start = self.samplenum
176 self.pend_addr = bus_addr
177
178 def on_cycle_end(self):
8830db5d 179 self.instr_len += 1
26abbf37
DE
180 self.op_state = getattr(self, 'on_state_' + self.op_state)()
181 if self.ann_dasm is not None:
182 self.put_disasm()
183 if self.op_state == OpState.RESTART:
184 self.op_state = self.on_state_IDLE()
185
186 if self.ann_data is not None:
187 self.put_text(self.data_start, self.ann_data,
188 '{:02X}'.format(self.pend_data))
189 self.data_start = self.samplenum
190 self.pend_data = self.bus_data
191 self.ann_data = ann_data_cycle_map[self.prev_cycle]
192
193 def on_cycle_trans(self):
194 self.put_text(self.samplenum - 1, Ann.WARN,
195 'Illegal transition between control states')
196 self.pend_addr = None
197 self.ann_data = None
198 self.ann_dasm = None
199
200 def put_disasm(self):
8830db5d
DE
201 text = formatter.format(self.mnemonic, r=self.arg_reg, d=self.arg_dis,
202 j=self.arg_dis+self.instr_len, i=self.arg_imm,
aef3c109 203 ro=self.arg_read, wo=self.arg_write)
26abbf37
DE
204 self.put_text(self.dasm_start, self.ann_dasm, text)
205 self.ann_dasm = None
206 self.dasm_start = self.samplenum
207
208 def put_text(self, ss, ann_idx, ann_text):
209 self.put(ss, self.samplenum, self.out_ann, [ann_idx, [ann_text]])
210
211 def on_state_IDLE(self):
212 if self.prev_cycle != Cycle.FETCH:
213 return OpState.IDLE
214 self.want_dis = 0
215 self.want_imm = 0
216 self.want_read = 0
217 self.want_write = 0
218 self.want_wr_be = False
219 self.op_repeat = False
220 self.arg_dis = 0
221 self.arg_imm = 0
222 self.arg_read = 0
223 self.arg_write = 0
224 self.arg_reg = ''
225 self.mnemonic = ''
226 self.instr_pend = False
227 self.read_pend = False
228 self.write_pend = False
229 self.dasm_start = self.samplenum
230 self.op_prefix = 0
8830db5d 231 self.instr_len = 0
26abbf37
DE
232 if self.bus_data in (0xCB, 0xED, 0xDD, 0xFD):
233 return OpState.PRE1
234 else:
235 return OpState.OPCODE
236
237 def on_state_PRE1(self):
238 if self.prev_cycle != Cycle.FETCH:
239 self.mnemonic = 'Prefix not followed by fetch'
240 self.ann_dasm = Ann.WARN
241 return OpState.RESTART
242 self.op_prefix = self.pend_data
243 if self.op_prefix in (0xDD, 0xFD):
244 if self.bus_data == 0xCB:
245 return OpState.PRE2
246 if self.bus_data in (0xDD, 0xED, 0xFD):
247 return OpState.PRE1
248 return OpState.OPCODE
249
250 def on_state_PRE2(self):
251 if self.prev_cycle != Cycle.MEMRD:
252 self.mnemonic = 'Missing displacement'
253 self.ann_dasm = Ann.WARN
254 return OpState.RESTART
255 self.op_prefix = (self.op_prefix << 8) | self.pend_data
256 return OpState.PREDIS
257
258 def on_state_PREDIS(self):
259 if self.prev_cycle != Cycle.MEMRD:
260 self.mnemonic = 'Missing opcode'
261 self.ann_dasm = Ann.WARN
262 return OpState.RESTART
263 self.arg_dis = signed_byte(self.pend_data)
264 return OpState.OPCODE
265
266 def on_state_OPCODE(self):
267 (table, self.arg_reg) = instr_table_by_prefix[self.op_prefix]
268 self.op_prefix = 0
269 instruction = table.get(self.pend_data, None)
270 if instruction is None:
271 self.mnemonic = 'Invalid instruction'
272 self.ann_dasm = Ann.WARN
273 return OpState.RESTART
274 (self.want_dis, self.want_imm, self.want_read, want_write,
275 self.op_repeat, self.mnemonic) = instruction
276 self.want_write = abs(want_write)
277 self.want_wr_be = (want_write < 0)
278 if self.want_dis > 0:
279 return OpState.POSTDIS
280 if self.want_imm > 0:
281 return OpState.IMM1
282 self.ann_dasm = Ann.INSTR
283 if self.want_read > 0 and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
284 return OpState.ROP1
285 if self.want_write > 0 and self.prev_cycle in (Cycle.MEMWR, Cycle.IOWR):
286 return OpState.WOP1
287 return OpState.RESTART
288
289 def on_state_POSTDIS(self):
290 self.arg_dis = signed_byte(self.pend_data)
291 if self.want_imm > 0:
292 return OpState.IMM1
293 self.ann_dasm = Ann.INSTR
294 if self.want_read > 0:
295 return OpState.ROP1
296 if self.want_write > 0:
297 return OpState.WOP1
298 return OpState.RESTART
299
300 def on_state_IMM1(self):
301 self.arg_imm = self.pend_data
302 if self.want_imm > 1:
303 return OpState.IMM2
304 self.ann_dasm = Ann.INSTR
305 if self.want_read > 0:
306 return OpState.ROP1
307 if self.want_write > 0:
308 return OpState.WOP1
309 return OpState.RESTART
310
311 def on_state_IMM2(self):
312 self.arg_imm |= self.pend_data << 8
313 self.ann_dasm = Ann.INSTR
314 if self.want_read > 0:
315 return OpState.ROP1
316 if self.want_write > 0:
317 return OpState.WOP1
318 return OpState.RESTART
319
320 def on_state_ROP1(self):
321 self.arg_read = self.pend_data
322 if self.want_write > 0:
323 return OpState.WOP1
324 if self.want_read > 1:
325 return OpState.ROP2
326 if self.op_repeat and self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
327 return OpState.ROP1
328 self.mnemonic = '{ro:02X}'
329 self.ann_dasm = Ann.ROP
330 return OpState.RESTART
331
332 def on_state_ROP2(self):
333 self.arg_read |= self.pend_data << 8
334 self.mnemonic = '{ro:04X}'
335 self.ann_dasm = Ann.ROP
336 if self.want_write > 0:
337 return OpState.WOP1
338 return OpState.RESTART
339
340 def on_state_WOP1(self):
341 self.arg_write = self.pend_data
342 if self.want_read > 1:
343 return OpState.ROP2
344 if self.want_write > 1:
345 return OpState.WOP2
346 if self.want_read > 0 and self.op_repeat and \
347 self.prev_cycle in (Cycle.MEMRD, Cycle.IORD):
348 return OpState.ROP1
349 self.mnemonic = '{wo:02X}'
350 self.ann_dasm = Ann.WOP
351 return OpState.RESTART
352
353 def on_state_WOP2(self):
354 if self.want_wr_be:
355 self.arg_write = (self.arg_write << 8) | self.pend_data
356 else:
357 self.arg_write |= self.pend_data << 8
358 self.mnemonic = '{wo:04X}'
359 self.ann_dasm = Ann.WOP
360 return OpState.RESTART