summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/pic16/pic16d.cpp
blob: 65f28f863cd74b2378d5fa26ecaaaf98ebb7580c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

// PIC16 generic disassembler, with extended opcodes

#include "emu.h"
#include "pic16d.h"

u32 pic16_disassembler::opcode_alignment() const
{
	return 1;
}

std::string pic16_disassembler::freg(u16 opcode) const
{
	return util::string_format("f_%02x", opcode & 0x7f);
}

std::string pic16_disassembler::imm8(u16 opcode)
{
	return util::string_format("%02x", opcode & 0xff);
}

std::string pic16_disassembler::imm7(u16 opcode)
{
	return util::string_format("%02x", opcode & 0x7f);
}

std::string pic16_disassembler::imm6(u16 opcode)
{
	return util::string_format("%02x", opcode & 0x3f);
}

std::string pic16_disassembler::imm6s(u16 opcode)
{
	if(opcode & 0x20)
		return util::string_format("-%02x", (-opcode) & 0x1f);
	else
		return util::string_format("%02x", opcode & 0x1f);
}

std::string pic16_disassembler::imm5(u16 opcode)
{
	return util::string_format("%02x", opcode & 0x1f);
}

std::string pic16_disassembler::rel9(u16 opcode, u16 pc)
{
	u16 off = opcode & 0x1ff;
	if(off & 0x100)
		off -= 0x200;
	return util::string_format("%04x", (pc + off + 1) & 0x7fff);
}

std::string pic16_disassembler::abs11(u16 opcode, u16 pc) const
{
	return util::string_format("%04x", (pc & 0x7800) | (opcode & 0x7ff));
}

#define P std::ostream &stream, const pic16_disassembler *d, u16 opcode, u16 pc
const pic16_disassembler::instruction pic16_disassembler::instructions[] {
	{ 0x0700, 0x3f80, [](P) -> u32 { util::stream_format(stream, "addwf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0780, 0x3f80, [](P) -> u32 { util::stream_format(stream, "addwf %s",     d->freg(opcode)); return 1; } },
	{ 0x3d00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "addwfc %s, w", d->freg(opcode)); return 1; } },
	{ 0x3d80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "addwfc %s",    d->freg(opcode)); return 1; } },
	{ 0x0500, 0x3f80, [](P) -> u32 { util::stream_format(stream, "andwf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0580, 0x3f80, [](P) -> u32 { util::stream_format(stream, "andwf %s",     d->freg(opcode)); return 1; } },
	{ 0x3700, 0x3f80, [](P) -> u32 { util::stream_format(stream, "asrf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x3780, 0x3f80, [](P) -> u32 { util::stream_format(stream, "asrf %s",      d->freg(opcode)); return 1; } },
	{ 0x3500, 0x3f80, [](P) -> u32 { util::stream_format(stream, "lslf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x3580, 0x3f80, [](P) -> u32 { util::stream_format(stream, "lslf %s",      d->freg(opcode)); return 1; } },
	{ 0x3600, 0x3f80, [](P) -> u32 { util::stream_format(stream, "lsrf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x3680, 0x3f80, [](P) -> u32 { util::stream_format(stream, "lsrf %s",      d->freg(opcode)); return 1; } },
	{ 0x0180, 0x3f80, [](P) -> u32 { util::stream_format(stream, "clrf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x0100, 0x3ffc, [](P) -> u32 { util::stream_format(stream, "clrw"                      ); return 1; } },
	{ 0x0900, 0x3f80, [](P) -> u32 { util::stream_format(stream, "comf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x0980, 0x3f80, [](P) -> u32 { util::stream_format(stream, "comf %s",      d->freg(opcode)); return 1; } },
	{ 0x0300, 0x3f80, [](P) -> u32 { util::stream_format(stream, "decf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x0380, 0x3f80, [](P) -> u32 { util::stream_format(stream, "decf %s",      d->freg(opcode)); return 1; } },
	{ 0x0a00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "incf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x0a80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "incf %s",      d->freg(opcode)); return 1; } },
	{ 0x0400, 0x3f80, [](P) -> u32 { util::stream_format(stream, "iorwf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0480, 0x3f80, [](P) -> u32 { util::stream_format(stream, "iorwf %s",     d->freg(opcode)); return 1; } },
	{ 0x0800, 0x3f80, [](P) -> u32 { util::stream_format(stream, "movf %s, w",   d->freg(opcode)); return 1; } },
	{ 0x0880, 0x3f80, [](P) -> u32 { util::stream_format(stream, "movf %s",      d->freg(opcode)); return 1; } },
	{ 0x0080, 0x3f80, [](P) -> u32 { util::stream_format(stream, "movwf %s",     d->freg(opcode)); return 1; } },
	{ 0x0d00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "rlf %s, w",    d->freg(opcode)); return 1; } },
	{ 0x0d80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "rlf %s",       d->freg(opcode)); return 1; } },
	{ 0x0c00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "rrf %s, w",    d->freg(opcode)); return 1; } },
	{ 0x0c80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "rrf %s",       d->freg(opcode)); return 1; } },
	{ 0x0200, 0x3f80, [](P) -> u32 { util::stream_format(stream, "subwf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0280, 0x3f80, [](P) -> u32 { util::stream_format(stream, "subwf %s",     d->freg(opcode)); return 1; } },
	{ 0x3b00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "subwfb %s, w", d->freg(opcode)); return 1; } },
	{ 0x3b80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "subwfb %s",    d->freg(opcode)); return 1; } },
	{ 0x0e00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "swapf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0e80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "swapf %s",     d->freg(opcode)); return 1; } },
	{ 0x0600, 0x3f80, [](P) -> u32 { util::stream_format(stream, "xorwf %s, w",  d->freg(opcode)); return 1; } },
	{ 0x0680, 0x3f80, [](P) -> u32 { util::stream_format(stream, "xorwf %s",     d->freg(opcode)); return 1; } },

	{ 0x0b00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "decfsz %s, w", d->freg(opcode)); return 1; } },
	{ 0x0b80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "decfsz %s",    d->freg(opcode)); return 1; } },
	{ 0x0f00, 0x3f80, [](P) -> u32 { util::stream_format(stream, "incfsz %s, w", d->freg(opcode)); return 1; } },
	{ 0x0f80, 0x3f80, [](P) -> u32 { util::stream_format(stream, "incfsz %s",    d->freg(opcode)); return 1; } },

	{ 0x1000, 0x3c00, [](P) -> u32 { util::stream_format(stream, "bcf %d, %s",   (opcode >> 7) & 7, d->freg(opcode)); return 1; } },
	{ 0x1400, 0x3c00, [](P) -> u32 { util::stream_format(stream, "bsf %d, %s",   (opcode >> 7) & 7, d->freg(opcode)); return 1; } },

	{ 0x1800, 0x3c00, [](P) -> u32 { util::stream_format(stream, "btfsc %d, %s", (opcode >> 7) & 7, d->freg(opcode)); return 1; } },
	{ 0x1c00, 0x3c00, [](P) -> u32 { util::stream_format(stream, "btfss %d, %s", (opcode >> 7) & 7, d->freg(opcode)); return 1; } },

	{ 0x3e00, 0x3f00, [](P) -> u32 { util::stream_format(stream, "addlw %s", imm8(opcode)); return 1; } },
	{ 0x3900, 0x3f00, [](P) -> u32 { util::stream_format(stream, "andlw %s", imm8(opcode)); return 1; } },
	{ 0x3800, 0x3f00, [](P) -> u32 { util::stream_format(stream, "iorlw %s", imm8(opcode)); return 1; } },
	{ 0x0020, 0x3fe0, [](P) -> u32 { util::stream_format(stream, "movlb %s", imm5(opcode)); return 1; } },
	{ 0x3180, 0x3f80, [](P) -> u32 { util::stream_format(stream, "movlp %s", imm7(opcode)); return 1; } },
	{ 0x3000, 0x3f00, [](P) -> u32 { util::stream_format(stream, "movlw %s", imm8(opcode)); return 1; } },
	{ 0x3c00, 0x3f00, [](P) -> u32 { util::stream_format(stream, "sublw %s", imm8(opcode)); return 1; } },
	{ 0x3a00, 0x3f00, [](P) -> u32 { util::stream_format(stream, "xorlw %s", imm8(opcode)); return 1; } },

	{ 0x3200, 0x3e00, [](P) -> u32 { util::stream_format(stream, "bra %s",  rel9(opcode,  pc)   ); return 1; } },
	{ 0x000b, 0x3fff, [](P) -> u32 { util::stream_format(stream, "brw"                          ); return 1; } },
	{ 0x2000, 0x3800, [](P) -> u32 { util::stream_format(stream, "call %s", d->abs11(opcode, pc)); return 1 | STEP_OVER; } },
	{ 0x000a, 0x3fff, [](P) -> u32 { util::stream_format(stream, "callw"                        ); return 1 | STEP_OVER; } },
	{ 0x2800, 0x3800, [](P) -> u32 { util::stream_format(stream, "goto %s", d->abs11(opcode, pc)); return 1; } },
	{ 0x0009, 0x3fff, [](P) -> u32 { util::stream_format(stream, "retfie"                       ); return 1 | STEP_OUT; } },
	{ 0x3400, 0x3f00, [](P) -> u32 { util::stream_format(stream, "retlw %s", imm8(opcode)       ); return 1 | STEP_OUT; } },
	{ 0x0008, 0x3fff, [](P) -> u32 { util::stream_format(stream, "return"                       ); return 1 | STEP_OUT; } },

	{ 0x0064, 0x3fff, [](P) -> u32 { util::stream_format(stream, "clrwdt"                    ); return 1; } },
	{ 0x0000, 0x3fff, [](P) -> u32 { util::stream_format(stream, "nop"                       ); return 1; } },
	{ 0x0062, 0x3fff, [](P) -> u32 { util::stream_format(stream, "option"                    ); return 1; } },
	{ 0x0001, 0x3fff, [](P) -> u32 { util::stream_format(stream, "reset"                     ); return 1; } },
	{ 0x0063, 0x3fff, [](P) -> u32 { util::stream_format(stream, "sleep"                     ); return 1; } },
	{ 0x0065, 0x3fff, [](P) -> u32 { util::stream_format(stream, "tris a"                    ); return 1; } },
	{ 0x0066, 0x3fff, [](P) -> u32 { util::stream_format(stream, "tris b"                    ); return 1; } },
	{ 0x0067, 0x3fff, [](P) -> u32 { util::stream_format(stream, "tris c"                    ); return 1; } },

	{ 0x3100, 0x3fc0, [](P) -> u32 { util::stream_format(stream, "addfsr fsr0, %s", imm6(opcode)); return 1; } },
	{ 0x3140, 0x3fc0, [](P) -> u32 { util::stream_format(stream, "addfsr fsr1, %s", imm6(opcode)); return 1; } },
	{ 0x0010, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw ++fsr0"                 ); return 1; } },
	{ 0x0011, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw --fsr0"                 ); return 1; } },
	{ 0x0012, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw fsr0++"                 ); return 1; } },
	{ 0x0013, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw fsr0--"                 ); return 1; } },
	{ 0x0014, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw ++fsr1"                 ); return 1; } },
	{ 0x0015, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw --fsr1"                 ); return 1; } },
	{ 0x0016, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw fsr1++"                 ); return 1; } },
	{ 0x0017, 0xffff, [](P) -> u32 { util::stream_format(stream, "moviw fsr1--"                 ); return 1; } },
	{ 0x3f00, 0xffc0, [](P) -> u32 { util::stream_format(stream, "moviw %s[fsr0]", imm6s(opcode)); return 1; } },
	{ 0x3f40, 0xffc0, [](P) -> u32 { util::stream_format(stream, "moviw %s[fsr1]", imm6s(opcode)); return 1; } },
	{ 0x0018, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi ++fsr0"                 ); return 1; } },
	{ 0x0019, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi --fsr0"                 ); return 1; } },
	{ 0x001a, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi fsr0++"                 ); return 1; } },
	{ 0x001b, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi fsr0--"                 ); return 1; } },
	{ 0x001c, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi ++fsr1"                 ); return 1; } },
	{ 0x001d, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi --fsr1"                 ); return 1; } },
	{ 0x001e, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi fsr1++"                 ); return 1; } },
	{ 0x001f, 0xffff, [](P) -> u32 { util::stream_format(stream, "movwi fsr1--"                 ); return 1; } },
	{ 0x3f80, 0xffc0, [](P) -> u32 { util::stream_format(stream, "movwi %s[fsr0]", imm6s(opcode)); return 1; } },
	{ 0x3fc0, 0xffc0, [](P) -> u32 { util::stream_format(stream, "movwi %s[fsr1]", imm6s(opcode)); return 1; } },

	{ 0x0000, 0x0000, [](P) -> u32 { util::stream_format(stream, "?%04x",   opcode); return 1; } },
};

#undef P

offs_t pic16_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
{
	u16 opcode = opcodes.r16(pc);

	for(u32 i=0;; i++)
		if((opcode & instructions[i].mask) == instructions[i].value)
			return instructions[i].cb(stream, this, opcode, pc) | SUPPORTED;
	return 0;
}