]> sigrok.org Git - libsigrokdecode.git/blob - decoders/z80/pd.py
z80: Output jump offsets relative to instruction start.
[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 import string
24
25 class Ann:
26     ADDR, MEMRD, MEMWR, IORD, IOWR, INSTR, ROP, WOP, WARN = range(9)
27 class Row:
28     ADDRBUS, DATABUS, INSTRUCTIONS, OPERANDS, WARNINGS = range(5)
29 class Pin:
30     D0, D7 = 0, 7
31     M1, RD, WR, MREQ, IORQ = range(8, 13)
32     A0, A15 = 13, 28
33 class Cycle:
34     NONE, MEMRD, MEMWR, IORD, IOWR, FETCH, INTACK = range(7)
35
36 class 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
51 # Provide custom format type 'H' for hexadecimal output
52 # with leading decimal digit (assembler syntax).
53 class 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
61 formatter = AsmFormatter()
62
63 ann_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
72 def 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
78 def signed_byte(byte):
79     return byte if byte < 128 else byte - 256
80
81 class 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 = [
91         {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data bus line %d' % i}
92             for i in range(8)
93     ] + [
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'},
101     ] + [
102         {'id': 'a%d' % i, 'name': 'A%d' % i, 'desc': 'Address bus line %d' % i}
103             for i in range(16)
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
142         self.instr_len  = 0
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):
179         self.instr_len += 1
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):
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,
203                                 ro=self.arg_read, wo=self.arg_write)
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
231         self.instr_len  = 0
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