summaryrefslogtreecommitdiffstats
path: root/main.c
blob: b6e8a2c0994e9713dce218d7aa5f4ff585676f37 (plain)
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;
}