/* 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 .
*/
#include
#include
#include
#include
#include
#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;
}