This repository has been archived by the owner on Mar 11, 2020. It is now read-only.
/
pic_disasm.c
160 lines (137 loc) · 6.54 KB
/
pic_disasm.c
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
/*
* vPICdisasm - PIC program disassembler.
* Written by Vanya A. Sergeev - <vsergeev@gmail.com>
*
* Copyright (C) 2007-2011 Vanya A. Sergeev
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* pic_disasm.c - PIC instruction disassembly into disassembledInstruction structure.
*
*/
#include <stdlib.h>
#include <stdint.h>
#include "pic_disasm.h"
#include "errorcodes.h"
/* Array of PIC instruction sets as defined in pic_instructionset.c,
* enumerated by PIC_Instruction_Set_Index enum in pic_disasm.h */
extern instructionSetInfo allInstructionSets[];
/* Disassembles/decodes operands back to their original form. */
static int disassembleOperands(disassembledInstruction *dInstruction);
/* Extracts certain bits of data from a mask, used to extract operands from their encoding in the opcode. */
static uint16_t extractDataFromMask(uint16_t data, uint16_t mask);
/* Look up an instruction by it's opcode in the instructionSet,
* starting from index offset. Always returns a valid instruction
* index because the last instruction in the instruction set database
* is set to be a generic data word (data). */
static int lookupInstruction(uint16_t opcode, int offset, int instructionSetIndex);
/* Disassembles an assembled instruction, including its operands. */
int disassembleInstruction(disassembledInstruction *dInstruction, const assembledInstruction *aInstruction, int instructionSetIndex) {
int instructionIndex, i;
if (dInstruction == NULL)
return ERROR_INVALID_ARGUMENTS;
if (instructionSetIndex != PIC_BASELINE &&
instructionSetIndex != PIC_MIDRANGE &&
instructionSetIndex != PIC_MIDRANGE_ENHANCED)
return ERROR_INVALID_ARGUMENTS;
/* Look up the instruction */
instructionIndex = lookupInstruction(aInstruction->opcode, 0, instructionSetIndex);
/* Copy over the address, and reference to the instruction, set
* the equivilant-encoded but different instruction to NULL for now. */
dInstruction->address = aInstruction->address;
dInstruction->instruction = &(allInstructionSets[instructionSetIndex].instructionSet[instructionIndex]);
dInstruction->alternateInstruction = NULL;
/* Copy out each operand, extracting the operand data from the original
* opcode using the operand mask. */
for (i = 0; i < allInstructionSets[instructionSetIndex].instructionSet[instructionIndex].numOperands; i++)
dInstruction->operands[i] = extractDataFromMask(aInstruction->opcode, dInstruction->instruction->operandMasks[i]);
/* Disassemble operands */
if (disassembleOperands(dInstruction) < 0)
return ERROR_INVALID_ARGUMENTS; /* Only possible error for disassembleOperands() */
return 0;
}
/* Extracts certain bits of data from a mask, used to extract operands from their encoding in the opcode. */
static uint16_t extractDataFromMask(uint16_t data, uint16_t mask) {
int i, j;
uint16_t result = 0;
/* i counts through every bit of the data,
* j counts through every bit of the data we're copying out. */
for (i = 0, j = 0; i < 16; i++) {
/* If the mask has a bit in this position */
if (mask & (1<<i)) {
/* If there is a data bit with this mask bit,
* then toggle that bit in the extracted data (result).
* Notice that it uses its own bit counter j. */
if (((mask & (1<<i)) & data) != 0)
result |= (1<<j);
/* Increment the extracted data bit count. */
j++;
}
}
return result;
}
/* Look up an instruction by it's opcode in the instructionSet,
* starting from index offset. Always returns a valid instruction
* index because the last instruction in the instruction set database
* is set to be a generic data word (data). */
static int lookupInstruction(uint16_t opcode, int offset, int instructionSetIndex) {
uint16_t opcodeSearch;
int instructionIndex, i;
for (instructionIndex = offset; instructionIndex < allInstructionSets[instructionSetIndex].numInstructions; instructionIndex++) {
opcodeSearch = opcode;
/* We want to mask out all of the operands. We don't count up to
* instructionSet[instructionIndex].numOperands because in some instructions
* we have the "x" don't-care bits that we want to mask out. */
for (i = 0; i < PIC_MAX_NUM_OPERANDS; i++)
opcodeSearch &= ~(allInstructionSets[instructionSetIndex].instructionSet[instructionIndex].operandMasks[i]);
if (opcodeSearch == allInstructionSets[instructionSetIndex].instructionSet[instructionIndex].opcodeMask)
break;
}
/* It's impossible not to find an instruction, because the last instruction "data",
* specifies a word of data at the addresses, instead of an instruction.
* Its operand 2 mask, 0x0000, will set opcode search to 0x0000, and this will always
* match with the opcodeMask of 0x0000. */
return instructionIndex;
}
/* Disassembles/decodes operands back to their original form. */
static int disassembleOperands(disassembledInstruction *dInstruction) {
int i;
uint16_t msb;
/* This should never happen */
if (dInstruction == NULL)
return ERROR_INVALID_ARGUMENTS;
if (dInstruction->instruction == NULL)
return ERROR_INVALID_ARGUMENTS;
/* For each operand, decode its original value. */
for (i = 0; i < dInstruction->instruction->numOperands; i++) {
switch (dInstruction->instruction->operandTypes[i]) {
case OPERAND_SIGNED_LITERAL:
case OPERAND_RELATIVE_ADDRESS:
/* We got lucky, because it turns out that in all of the masks
* for relative jumps / signed literals, the bits occupy the
* lowest positions continuously (no breaks in the bit string). */
/* Calculate the most significant bit of this signed data */
msb = (dInstruction->instruction->operandMasks[i] + 1) >> 1;
/* Check if the most significant bit is set (the number is negative) */
if ((dInstruction->operands[i] & msb) != 0) {
/* If so, recover the data and set the operand negative. */
dInstruction->operands[i] = (~dInstruction->operands[i]+1)&(dInstruction->instruction->operandMasks[i]);
dInstruction->operands[i] = -dInstruction->operands[i];
}
default:
break;
}
}
return 0;
}