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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
/* Copyright (C) 2020 Gentoo-libre Install
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#define MAX 80
//opcodes are two bytes and stored big endian.
int main()
{
uint8_t opcode[2];
int jmpNum = 0;
int jmp[MAX]; //where all the jmp positions are stored
FILE *input = fopen("TETRIS", "r");
if (!input){fprintf(stderr, "Program not found.\n"); return 1;}
fseek(input, 0L, SEEK_END);
int size = ftell(input);
rewind(input);
char *program = malloc(sizeof(char)*size);
if (!program){fprintf(stderr, "Malloc failed!\n"); exit(1);}
int result = fread(program, 1, size, input);
if (!result){fprintf(stderr, "Fread failed!\n"); exit(1);}
fclose(input);
bool isData = false; //set as soon as the end of data is detected.
FILE *output = fopen("output.asm", "w");
int position = 0;//position in program
position = 0;//reset position for 2nd pass where deassembly actually happens.
while(position < size)
{
if (!isData){fprintf(output, "%d:\t", position);}
else
{
fprintf(output,"Data:\n");
for (;position < size; position += 2)
{
//very slow but seems to work
for (int i = 0; i < jmpNum; ++i)
{
if (jmp[i] == position)//exit data mode if next instruction is called by the program
{
isData = false;
fprintf(output, "%d:\t", position);
goto keep_going;
}
}
fprintf(output,"%02X%02X\n", program[position] & 0xFF,program[position+1] & 0xFF);
}
fclose(output);
free(program);
exit(0);
}
keep_going: memcpy(opcode, program+position, 2*sizeof(char));
position += 2;//read next opcode next time
switch ((opcode[0] & 0xF0) >> 4)
{
case 0x0://0NNN or 00E0 or 00EE
switch(opcode[1])
{
case 0xE0://00E0
fprintf(output, "00E0\t; Display clear\n");
break;
case 0xEE://00EE
fprintf(output, "00EE\t; return\n");
//check if next instruction can be jpm'd to (and therefore is not data) or assume that is data if there is no such jmp
isData = true;
for (int i = 0; i < jmpNum; ++i)
{
if (jmp[i] == position){isData = false; break;}//next instruction is not data as the program jumps to it
}
break;
default://0NNN
fprintf(output, "Call RCA 1802 program at %d\n", (opcode[0]&0x0F)*0x100+opcode[1]-512);
break;
}
break;
//1NNN
case 0x1:
fprintf(output,"%02X%02X\t; Jump to: %d\n",opcode[0],opcode[1],(opcode[0] & 0x0F)*0x100+opcode[1]-512);
if (jmpNum < MAX){jmp[jmpNum++] = (opcode[0] & 0x0F)*0x100+opcode[1]-512;}
break;
//2NNN
case 0x2:
fprintf(output,"%02X%02X\t; Call subroute at %d\n", opcode[0], opcode[1], (opcode[0] & 0x0F)*0x100+opcode[1]-512);
if (jmpNum < MAX){jmp[jmpNum++] = (opcode[0] & 0x0F)*0x100+opcode[1]-512;}
break;
case 0x3://3XNN
fprintf(output,"%02X%02X\t; if (V%X==%d): skip the next opcode.\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1]);
if (jmpNum < MAX){jmp[jmpNum++] = position+2;}
break;
case 0x4://4XNN
fprintf(output,"%02X%02X\t; if (V%X!=%d): skip the next opcode.\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1]);
if (jmpNum < MAX){jmp[jmpNum++] = position+2;}
break;
case 0x5://5XY0
fprintf(output,"%02X%02X\t; if (V%X==V%X): skip the next opcode.\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1] & 0xF0);
if (jmpNum < MAX){jmp[jmpNum++] = position+2;}
break;
case 0x6://6XNN
fprintf(output,"%02X%02X\t; V%X=%d\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1]);
break;
case 0x7://7XNN
fprintf(output,"%02X%02X\t; V%X += %d\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1]);
break;
case 0x8://8XY0 or 8XY1 or 8XY2 or 8XY3 or 8XY4 or 8XY5 or 8XY6 or 8XY7 or 8XYE
switch(opcode[1] & 0x0F)
{
case 0x0://8XY0
fprintf(output,"%02X%02X\t; V%X = V%X\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x1://8XY1
fprintf(output,"%02X%02X\t; V%X = V%X|V%X\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x2://8XY2
fprintf(output,"%02X%02X\t; V%X = V%X&V%X\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x3://8XY3
fprintf(output,"%02X%02X\t; V%X = V%X^V%X\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x4://8XY4
fprintf(output,"%02X%02X\t; V%X += V%X\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x5://8XY5
fprintf(output,"%02X%02X\t; V%X -= V%X: VF is set to 0 if there's a borrow and to 1 when there isn't.\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0x6://8XY6
fprintf(output,"%02X%02X\t; VF = V%X>>7 then V%X >>= 1 \n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[0] & 0x0F);
//Stores the least significant bit of VX in VF
//and then shifts VX to the right by 1
//OR
//Set register VF to the least significant bit in V[x]
//Store the value of register VY shifted right one bit in register VX
break;
case 0x7://8XY7
fprintf(output,"%02X%02X\t; V%X = V%X - V%X: VF is set to 0 if there's a borrow and to 1 when there isn't.\n",opcode[0],opcode[1],opcode[0] & 0x0F,opcode[0] & 0x0F,opcode[1] & 0xF0);
//V[x] = V[y] - V[x]; //Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
break;
case 0xE://8XYE
fprintf(output,"%02X%02X\t; VF = V%X>>7 then V%X <<= 1;\n",opcode[0],opcode[1],opcode[0] & 0x0F,opcode[0] & 0x0F);
break;
default:
fprintf(stderr, "Illegal Instruction\n"); exit(1);
break;
}
break;
case 0x9://9XY0
fprintf(output,"%02X%02X\t; if (V%X != V%X): Skip next instruction.\n",opcode[0],opcode[1],opcode[0] & 0x0F, opcode[1] & 0xF0);
break;
case 0xA://ANNN
fprintf(output,"%02X%02X\t; I = %d\n",opcode[0],opcode[1],(opcode[0] & 0xF)*0x100+opcode[1]-512);
break;
case 0xB://BNNN
fprintf(output,"%02X%02X\t; Jump to V0+%d\n",opcode[0],opcode[1],(opcode[0] & 0xF)*0x100+opcode[1]-512);
break;
case 0xC://CXNN
fprintf(output,"%02X%02X\t; V%X = rand() & %d\n",opcode[0],opcode[1],opcode[0]&0x0F,opcode[1]);
break;
case 0xD://DXYN
fprintf(output,"%02X%02X\t; draw(X=V%X, Y=V%X, %d)\n",opcode[0],opcode[1],opcode[0]&0x0F,(opcode[1]&0xF0)>>4,opcode[1]&0x0F);
break;
case 0xE://EX9E or EXA1
switch(opcode[1])
{
case 0x9E://EX9E
fprintf(output,"%02X%02X\t; Skip next instruction if key in V%X is held down\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0xA1://EXA1
fprintf(output,"%02X%02X\t; Skip next instruction if key in V%X isn't held down\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
default:
fprintf(stderr, "Illegal Instruction\n"); exit(1);
break;
}
break;
case 0xF://FX07 or FX0A or FX15 or FX18 or FX1E or FX29 or FX33 or FX55 or FX65
switch (opcode[1])
{
case 0x07://FX07
fprintf(output,"%02X%02X\t; V%X = Delay timer.\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0x0A://FX0A
fprintf(output,"%02X%02X\t; V%X = 0-9 (from keypress and is a blocking operation).\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0x15://FX15
fprintf(output,"%02X%02X\t; Delay timer = V%X.\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0x18://FX18
fprintf(output,"%02X%02X\t; Sound timer = V%X.\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0x1E://FX1E
fprintf(output,"%02X%02X\t; VF = (I+V%X)>0xFFF.\n",opcode[0],opcode[1],opcode[0]&0x0F);
break;
case 0x29://FX29
fprintf(output,"%02X%02X\t; I=spriteChar(V%X): I equals a pointer to the character corresponding to the value in V%X.\n",opcode[0],opcode[1],opcode[0]&0x0F,opcode[0]&0x0F);
break;
case 0x33://FX33
fprintf(output,"%02X%02X\t; I = V%X / 100, I+1 = (V%X / 10) %% 10, I+2 = (V%X %% 100) %% 10\n",opcode[0],opcode[1],opcode[0]&0x0F,opcode[0]&0x0F,opcode[0]&0x0F);
break;
case 0x55://FX55
fprintf(output,"%02X%02X\t; Store V0 to V%X (including V%X) in memory starting at address I\n",opcode[0],opcode[1],opcode[0]&0x0F,opcode[0]&0x0F);
break;
case 0x65://FX65
fprintf(output,"%02X%02X\t; Fill V0 to V%X (including V%X) with values in memory starting at address I\n",opcode[0],opcode[1],opcode[0]&0x0F,opcode[0]&0x0F);
break;
default:
fprintf(stderr, "Illegal Instruction\n"); exit(1);
break;
}
break;
default:
fprintf(stderr, "Illegal Instruction\n"); exit(1);
break;
}
}
fclose(output);
free(program);
return 0;
}
|