1 #include "chip8.h"
2 #include "chip8_printer.h"
3 
4 #define SET_DECODE_TO(opmask, cb) m_opcodes[opmask] = [this](u16 opcode, const InstructionPtr& instruction) -> bool { return cb(opcode, instruction); };
5 
6 namespace REDasm {
7 
CHIP8Assembler()8 CHIP8Assembler::CHIP8Assembler(): AssemblerPlugin()
9 {
10     SET_DECODE_TO(0x0000, decode0xxx);
11     SET_DECODE_TO(0x1000, decode1xxx);
12     SET_DECODE_TO(0x2000, decode2xxx);
13     SET_DECODE_TO(0x3000, decode3xxx);
14     SET_DECODE_TO(0x4000, decode4xxx);
15     SET_DECODE_TO(0x5000, decode5xxx);
16     SET_DECODE_TO(0x6000, decode6xxx);
17     SET_DECODE_TO(0x7000, decode7xxx);
18     SET_DECODE_TO(0x8000, decode8xxx);
19     SET_DECODE_TO(0x9000, decode9xxx);
20     SET_DECODE_TO(0xA000, decodeAxxx);
21     SET_DECODE_TO(0xB000, decodeBxxx);
22     SET_DECODE_TO(0xC000, decodeCxxx);
23     SET_DECODE_TO(0xD000, decodeDxxx);
24     SET_DECODE_TO(0xE000, decodeExxx);
25     SET_DECODE_TO(0xF000, decodeFxxx);
26 }
27 
createPrinter(DisassemblerAPI * disassembler) const28 Printer *CHIP8Assembler::createPrinter(DisassemblerAPI *disassembler) const { return new CHIP8Printer(disassembler); }
29 
decodeInstruction(const BufferView & view,const InstructionPtr & instruction)30 bool CHIP8Assembler::decodeInstruction(const BufferView& view, const InstructionPtr &instruction)
31 {
32     u16be opcode = static_cast<u16be>(view);
33     instruction->id = opcode;
34     instruction->size = sizeof(u16);
35 
36     auto it = m_opcodes.find(opcode & 0xF000);
37 
38     if((it == m_opcodes.end()) || !it->second(opcode, instruction))
39         return false;
40 
41     return true;
42 }
43 
onDecoded(const InstructionPtr & instruction)44 void CHIP8Assembler::onDecoded(const InstructionPtr &instruction)
45 {
46     if(instruction->mnemonic == "rts")
47         instruction->type = InstructionType::Stop;
48     else if(instruction->mnemonic == "jmp")
49         instruction->type = InstructionType::Jump;
50     else if((instruction->mnemonic == "ske") || (instruction->mnemonic == "skne") || (instruction->mnemonic == "skp") || (instruction->mnemonic == "sknp"))
51         instruction->type = InstructionType::ConditionalJump;
52     else if(instruction->mnemonic == "call")
53         instruction->type = InstructionType::Call;
54     else if(instruction->mnemonic == "add")
55         instruction->type = InstructionType::Add;
56     else if(instruction->mnemonic == "sub")
57         instruction->type = InstructionType::Sub;
58     else if(instruction->mnemonic == "and")
59         instruction->type = InstructionType::And;
60     else if(instruction->mnemonic == "or")
61         instruction->type = InstructionType::Or;
62     else if(instruction->mnemonic == "xor")
63         instruction->type = InstructionType::Xor;
64     else if((instruction->mnemonic == "mov") || (instruction->mnemonic == "ldra"))
65         instruction->type = InstructionType::Load;
66     else if(instruction->mnemonic == "stra")
67         instruction->type = InstructionType::Store;
68     else if(instruction->mnemonic == "sys")
69         instruction->type = InstructionType::Privileged;
70 }
71 
decode0xxx(u16 opcode,const InstructionPtr & instruction) const72 bool CHIP8Assembler::decode0xxx(u16 opcode, const InstructionPtr &instruction) const
73 {
74     if(opcode == 0x00E0)
75         instruction->mnemonic = "cls";
76     else if(opcode == 0x00EE)
77         instruction->mnemonic = "rts";
78     else if(opcode == 0x00FB) // SuperChip only
79         instruction->mnemonic = "scright";
80     else if(opcode == 0x00FC) // SuperChip only
81         instruction->mnemonic = "scleft";
82     else if(opcode == 0x00FE) // SuperChip only
83         instruction->mnemonic = "low";
84     else if(opcode == 0x00FF) // SuperChip only
85         instruction->mnemonic = "high";
86     else if((opcode & 0x00F0) == 0x00C0) // SuperChip only
87     {
88         instruction->mnemonic = "scdown";
89         instruction->cnst(opcode & 0x000F);
90     }
91     else
92     {
93         instruction->mnemonic = "sys";
94         instruction->cnst(opcode & 0x0FFF);
95     }
96 
97     return true;
98 }
99 
decode1xxx(u16 opcode,const InstructionPtr & instruction) const100 bool CHIP8Assembler::decode1xxx(u16 opcode, const InstructionPtr &instruction) const
101 {
102     instruction->mnemonic = "jmp";
103     instruction->imm(opcode & 0x0FFF);
104     instruction->targetIdx(0);
105     return true;
106 }
107 
decode2xxx(u16 opcode,const InstructionPtr & instruction) const108 bool CHIP8Assembler::decode2xxx(u16 opcode, const InstructionPtr &instruction) const
109 {
110     instruction->mnemonic = "call";
111     instruction->imm(opcode & 0x0FFF);
112     instruction->targetIdx(0);
113     return true;
114 }
115 
decode3xxx(u16 opcode,const InstructionPtr & instruction) const116 bool CHIP8Assembler::decode3xxx(u16 opcode, const InstructionPtr &instruction) const
117 {
118     instruction->mnemonic = "ske";
119     instruction->reg((opcode & 0x0F00) >> 8);
120     instruction->imm(opcode & 0x00FF);
121     instruction->target(instruction->endAddress() + instruction->size);
122     return true;
123 }
124 
decode4xxx(u16 opcode,const InstructionPtr & instruction) const125 bool CHIP8Assembler::decode4xxx(u16 opcode, const InstructionPtr &instruction) const
126 {
127     instruction->mnemonic = "skne";
128     instruction->reg((opcode & 0x0F00) >> 8);
129     instruction->imm(opcode & 0x00FF);
130     instruction->target(instruction->endAddress() + instruction->size);
131     return true;
132 }
133 
decode5xxx(u16 opcode,const InstructionPtr & instruction) const134 bool CHIP8Assembler::decode5xxx(u16 opcode, const InstructionPtr &instruction) const
135 {
136     if((opcode & 0x000F) != 0)
137         return false;
138 
139     instruction->mnemonic = "ske";
140     instruction->reg((opcode & 0x0F00) >> 8);
141     instruction->reg((opcode & 0x00F0) >> 4);
142     instruction->target(instruction->endAddress() + instruction->size);
143     return true;
144 }
145 
decode6xxx(u16 opcode,const InstructionPtr & instruction) const146 bool CHIP8Assembler::decode6xxx(u16 opcode, const InstructionPtr &instruction) const
147 {
148     instruction->mnemonic = "mov";
149     instruction->reg((opcode & 0x0F00) >> 8);
150     instruction->cnst(opcode & 0x00FF);
151     return true;
152 }
153 
decode7xxx(u16 opcode,const InstructionPtr & instruction) const154 bool CHIP8Assembler::decode7xxx(u16 opcode, const InstructionPtr &instruction) const
155 {
156     instruction->mnemonic = "add";
157     instruction->reg((opcode & 0x0F00) >> 8);
158     instruction->cnst(opcode & 0x00FF);
159     return true;
160 }
161 
decode8xxx(u16 opcode,const InstructionPtr & instruction) const162 bool CHIP8Assembler::decode8xxx(u16 opcode, const InstructionPtr &instruction) const
163 {
164     u8 op = opcode & 0x000F;
165 
166     if(op == 0x0)
167         instruction->mnemonic = "mov";
168     else if(op == 0x1)
169         instruction->mnemonic = "or";
170     else if(op == 0x2)
171         instruction->mnemonic = "and";
172     else if(op == 0x3)
173         instruction->mnemonic = "xor";
174     else if(op == 0x4)
175         instruction->mnemonic = "add";
176     else if(op == 0x5)
177         instruction->mnemonic = "sub";
178     else if(op == 0x6)
179         instruction->mnemonic = "shr";
180     else if(op == 0x7)
181         instruction->mnemonic = "sub";
182     else if(op == 0xE)
183         instruction->mnemonic = "shl";
184     else
185         return false;
186 
187     instruction->reg((opcode & 0x0F00) >> 8);
188 
189     if((op != 0x6) && (op != 0xE)) // Skip 2nd operand if op == shift_instructions
190         instruction->reg((opcode & 0x00F0) >> 4);
191 
192     return true;
193 }
194 
decode9xxx(u16 opcode,const InstructionPtr & instruction) const195 bool CHIP8Assembler::decode9xxx(u16 opcode, const InstructionPtr &instruction) const
196 {
197     if((opcode & 0x000F) != 0)
198         return false;
199 
200     instruction->mnemonic = "skne";
201     instruction->reg((opcode & 0x0F00) >> 8);
202     instruction->reg((opcode & 0x00F0) >> 4);
203     instruction->target(instruction->endAddress() + instruction->size);
204     return true;
205 }
206 
decodeAxxx(u16 opcode,const InstructionPtr & instruction) const207 bool CHIP8Assembler::decodeAxxx(u16 opcode, const InstructionPtr &instruction) const
208 {
209     instruction->mnemonic = "mov";
210     instruction->reg(CHIP8_REG_I_ID, CHIP8_REG_I);
211     instruction->cnst(opcode & 0x0FFF);
212     return true;
213 }
214 
decodeBxxx(u16 opcode,const InstructionPtr & instruction) const215 bool CHIP8Assembler::decodeBxxx(u16 opcode, const InstructionPtr &instruction) const
216 {
217     instruction->mnemonic = "jmp";
218     instruction->disp(CHIP8_REG_V0_ID, opcode & 0x0FFF);
219     return true;
220 }
221 
decodeCxxx(u16 opcode,const InstructionPtr & instruction) const222 bool CHIP8Assembler::decodeCxxx(u16 opcode, const InstructionPtr &instruction) const
223 {
224     instruction->mnemonic = "rand";
225     instruction->reg((opcode & 0x0F00) >> 8);
226     instruction->cnst(opcode & 0x00FF);
227     return true;
228 }
229 
decodeDxxx(u16 opcode,const InstructionPtr & instruction) const230 bool CHIP8Assembler::decodeDxxx(u16 opcode, const InstructionPtr &instruction) const
231 {
232     instruction->mnemonic = "draw";
233     instruction->reg((opcode & 0x0F00) >> 8);
234     instruction->reg((opcode & 0x00F0) >> 4);
235     instruction->cnst(opcode & 0x000F);
236     return true;
237 }
238 
decodeExxx(u16 opcode,const InstructionPtr & instruction) const239 bool CHIP8Assembler::decodeExxx(u16 opcode, const InstructionPtr &instruction) const
240 {
241     u16 op = opcode & 0xFF;
242 
243     if(op == 0x9E)
244         instruction->mnemonic = "skp";
245     else if(op == 0xA1)
246         instruction->mnemonic = "sknp";
247 
248     instruction->reg((opcode & 0x0F00) >> 8, CHIP8_REG_K);
249     instruction->target(instruction->endAddress() + instruction->size);
250     return true;
251 }
252 
decodeFxxx(u16 opcode,const InstructionPtr & instruction) const253 bool CHIP8Assembler::decodeFxxx(u16 opcode, const InstructionPtr &instruction) const
254 {
255     u16 op = opcode & 0x00FF;
256 
257     if(op == 0x07)
258         instruction->mnemonic = "gdelay";
259     else if(op == 0x0A)
260         instruction->mnemonic = "wkey";
261     else if(op == 0x15)
262         instruction->mnemonic = "sdelay";
263     else if(op == 0x18)
264         instruction->mnemonic = "ssound";
265     else if(op == 0x1E)
266     {
267         instruction->mnemonic = "add";
268         instruction->reg(CHIP8_REG_I_ID, CHIP8_REG_I);
269     }
270     else if(op == 0x29)
271         instruction->mnemonic = "font";
272     else if(op == 0x30) // SuperChip only
273         instruction->mnemonic = "xfont";
274     else if(op == 0x33)
275         instruction->mnemonic = "bcd";
276     else if(op == 0x55)
277         instruction->mnemonic = "stra";
278     else if(op == 0x65)
279         instruction->mnemonic = "ldra";
280     else
281         return false;
282 
283     instruction->reg((opcode & 0x0F00) >> 8);
284     return true;
285 }
286 
287 } // namespace REDasm
288