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