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