// fixme: compile ranges for pass1 and pass2
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "opcodes.h"

FILE *inFile;
FILE *outFile;

uint16_t usage[0xff];   // is this opcode used? 256 opcodes in total

bool labels[0x400];   // label used flags, 64kb (65536) max code size
bool bank;            // bank select bit

//char stringBuffer[1024];

void decompile(uint8_t data[], uint16_t progCnt);

void partialDecompile(uint8_t data[], uint16_t progCnt);

void pass1();
void pass2();

void pass1() {
  for (uint16_t i = 0; i<=0x3ff; i++) {
    labels[i] = false;
  }
  uint16_t PC = 0;

  while ( true ) {
    uint8_t opcodeArray[2];
    uint16_t PCpass = 0;
    uint8_t retVal;
    uint8_t opCode;
    size_t err;

    //printf("PC: 0x%03X\n", PC);
    // firmware file size
    if (PC > 0x3FF) break;

    err = fread(&opCode, 1, 1, inFile);
    if (err != 1) {
      printf("Error reading from source in while loop pass1 opcode[0]!\n");
      exit(-1);
    }

    PCpass = PC;
    opcodeArray[0] = opCode;

    // do not decode because string is here
    if (PC >=0x300) {
      PC++;
      continue;
    }
    
    // no code after this address, do nothing
    if (PC <= 0x2E0) {
      for (uint8_t i = 1; i < opcodeLen[opCode]; i++ ) {
        err = fread(&retVal, 1, 1, inFile);
        if (err != 1) {
          printf("Error reading from source in while loop pass1 opcodes[]!\n");
          exit(-1);
        }
        opcodeArray[i] = retVal;
        PC++;
      }
      partialDecompile(opcodeArray, PCpass);
    }
    PC++;
  }
}

void pass2() {

  fprintf(outFile, "#include <stdio.h>\n");
  fprintf(outFile, "#include <stdlib.h>\n");
  fprintf(outFile, "#include <stdint.h>\n");
  fprintf(outFile, "#include <stdbool.h>\n");
  fprintf(outFile, "\n");
  fprintf(outFile, "// nice routines from friend\n");
  fprintf(outFile, "#define bitRead(value, bit) (((value) >> (bit)) & 0x01)\n");
  fprintf(outFile, "#define bitSet(value, bit) ((value) |= (1UL << (bit)))\n");
  fprintf(outFile, "#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))\n");
  fprintf(outFile, "#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))\n");
  fprintf(outFile, "#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))\n");
  fprintf(outFile, "\n");

  fprintf(outFile, "#define bit0 0\n");
  fprintf(outFile, "#define bit1 1\n");
  fprintf(outFile, "#define bit2 2\n");
  fprintf(outFile, "#define bit3 3\n");
  fprintf(outFile, "#define bit4 4\n");
  fprintf(outFile, "#define bit5 5\n");
  fprintf(outFile, "#define bit6 6\n");
  fprintf(outFile, "#define bit7 7\n");
  fprintf(outFile, "\n");
  
  fprintf(outFile, "uint8_t Port1, Port2;\n");
  fprintf(outFile, "uint8_t TCON, SCON, IntEnable, IntPriority, PSW;\n");
  
  fprintf(outFile, "\n");

  fprintf(outFile, "uint8_t RAM[0x100];\n");
  fprintf(outFile, "uint8_t accumulator, regB, tmp8;\n");
  fprintf(outFile, "uint8_t R0, R1, R2, R3, R4, R5, R6, R7;\n");
  fprintf(outFile, "uint16_t DPTR, tmp16;\n");
  fprintf(outFile, "bool carry, tmpCarry;\n");
  fprintf(outFile, "\n");

  fprintf(outFile, "int main(int argc, char **argv) {\n");
  fprintf(outFile, "\n");

  uint16_t PC = 0;

  while ( true ) {
    uint8_t opcodeArray[3];   // up to 3 bytes per opcode
    uint16_t PCpass = 0;    // program counter to paas to decompiler
    uint8_t retVal;         // return byte from input file for opcode array
    uint8_t opCode;     // opcode readed from input file (neet to rewrite/remove
    size_t err;         // for file handling

    // firmware file size
    if (PC > 0x3FF) break;

    err = fread(&opCode, 1, 1, inFile);
    if (err != 1) {
      printf("Error reading from source in while loop pass2 opcode[0]!\n");
      exit(-1);
    }
    //printf("label_%03X:\n", PC);

    PCpass = PC;
    opcodeArray[0] = opCode;

    // string, do not parse, write as text or hex
    if (PC >=0x300) {
      fprintf(outFile, "0x%02X, ", opcodeArray[0]);
      PC++;
      continue;
    }
    
    // if more, than code ends, so not analyze, or write as text
    if (PC <= 0x2E0) {
      // print label if detected or force to print first 0x25 labels (jump table)
      if ( labels[PC] || PC <= 0x07) {
        fprintf(outFile, "label_%03X:", PC);
      }
      
      // print opcode and bunary. useful for decompiler debug
      // and double check what opcode do
      fprintf(outFile, "\t// addr: %03X\topcodes: %02X", PCpass, opCode);
      // fill array with opcode bytes
      for (uint8_t i = 1; i < opcodeLen[opCode]; i++ ) {
        err = fread(&retVal, 1, 1, inFile);
        if (err != 1) {
          printf("Error reading from source in while loop pass2 opcodes[]!\n");
          exit(-1);
        }
        opcodeArray[i] = retVal;
        fprintf(outFile, " %02X", retVal);
        PC++;
      }
      fprintf(outFile, "\n\t");
      decompile(opcodeArray, PCpass);
    } else {    // no code, write as text
      if ( (opcodeArray[0] >= 0x20) && (opcodeArray[0] < 0x7F) ) {
        fprintf(outFile, "%c", opcodeArray[0]);
      }
    }
    PC++;
  }
  
  fprintf(outFile, ";\n");
  
/*
  printf("unused opcodes:\n");
  for (int i = 0; i <= 0xFF; i++) {
    if (usage[i] == 0 ) {
      printf("opcode: %02X\n", i);
    }
  }
  printf("\n");
*/

/*
  printf("used labels:\n");
  for (uint16_t i = 0; i <= 0xfff; i++) {
    if ( labels[i] ) {
      printf("label_0x%03X\n", i);
    }
  }
  printf("\n");
*/
}

int main(int argc, char **argv) {
  // do pass 1: analyze what labels are used
  // open input binary file for reading
  inFile = fopen(argv[1], "rb");
  if (inFile == NULL) {
    printf("Error opening file %s !\n", argv[1]);
    exit(-1);
  }

  pass1();
  // close input binary file because I am too lazy to use seek
  fclose(inFile);

  // do pass 2: ganerate C code in output file
  // reopen input binary file for reading because I am too lazy to use seek
  inFile = fopen(argv[1], "rb");
  if (inFile == NULL) {
    printf("Error opening file %s !\n", argv[1]);
    exit(-1);
  }

  // open result file for writing
  outFile = fopen(argv[2], "w");
  if (outFile == NULL) {
    printf("Error opening file %s !\n", argv[2]);
    exit(-1);
  }

  pass2();

  fclose(inFile);
  fclose(outFile);

  return 0;
}


// fixme: add relativeAddress() where nacessary
void decompile(uint8_t data[], uint16_t progCnt) {

  uint8_t tmp8;
  uint16_t tmp16;

  usage[data[0]]++;

  switch(data[0]) {
    case 0x00:    // NOP
      fprintf(outFile, "// NOP\n");
      break;
    case 0xB3:    // JMPP @A
      fprintf(outFile, "// Warning this is runtime stuff: jump to accumulator contents\n");
      fprintf(outFile, "// goto label_[(PC & 0xF00) | accumulator];\t// This is runtime stuff! Need some fixing!\n");
      break;
    case 0x04:    // JMP
    case 0x24:    // JMP
    case 0x44:    // JMP
    case 0x64:    // JMP
    case 0x84:    // JMP
    case 0xA4:    // JMP
    case 0xC4:    // JMP
    case 0xE4:    // JMP
      tmp16 = ((uint16_t)(data[0] & 0b11100000) << 3) + (uint16_t)data[1];
      tmp16 = ( (progCnt) & 0b1111100000000000) + tmp16;
      fprintf(outFile, "goto label_%03X; // jump\n", tmp16);
      break;
    case 0x14:    // CALL
    case 0x34:    // CALL
    case 0x54:    // CALL
    case 0x74:    // CALL
      tmp16 = ((uint16_t)(data[0] & 0b01100000) << 3) + (uint16_t)data[1];
      tmp16 = ( (progCnt) & 0b1111110000000000) + tmp16;
      fprintf(outFile, "label_%03X(); // call\n", tmp16);
      break;
    case 0x83:    // RET
      fprintf(outFile, "return; // return RET (opcode: 0x%02X)\n", data[0]);
      break;
    case 0x93:    // RETR
      fprintf(outFile, "return; // return RETR (opcode: 0x%02X)\n", data[0]);
      break;
    case 0xE8:    // DNJZ R0, addr
    case 0xE9:    // DNJZ R1, addr
    case 0xEA:    // DNJZ R2, addr
    case 0xEB:    // DNJZ R3, addr
    case 0xEC:    // DNJZ R4, addr
    case 0xED:    // DNJZ R5, addr
    case 0xEE:    // DNJZ R6, addr
    case 0xEF:    // DNJZ R7, addr
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "R%01X--;\n", tmp8);
      fprintf(outFile, "if (R%01X != 0) goto label_%03X;\n", tmp8, tmp16);
      break;
    case 0x12:    // JB0 addr
    case 0x32:    // JB1 addr
    case 0x52:    // JB2 addr
    case 0x72:    // JB3 addr
    case 0x92:    // JB4 addr
    case 0xB2:    // JB5 addr
    case 0xD2:    // JB6 addr
    case 0xF2:    // JB7 addr
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "if (bitRead(accumulator, 0x%01X) != 0) goto label_%03X;\n", tmp8, tmp16);
      break;
    case 0xC6:    // JC address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( carry ) goto label_%03X;\n", tmp16 );
      break;
    case 0x96:    // JNC address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( accumulator ) goto label_%03X;\n", tmp16 );
      break;
    case 0xF6:    // JZ address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( accumulator == 0 ) goto label_%03X;\n", tmp16 );
      break;
    case 0xE6:    // JNZ address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( carry == false ) goto label_%03X;\n", tmp16 );
      break;
    case 0x36:    // JT0 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( pinT0() ) goto label_%03X;\n", tmp16 );
      break;
    case 0x56:    // JT1 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( pinT1() ) goto label_%03X;\n", tmp16 );
      break;
    case 0x26:    // JNT0 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( pinT0() == false ) goto label_%03X;\n", tmp16 );
      break;
    case 0x46:    // JNT1 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( pinT1() == false ) goto label_%03X;\n", tmp16 );
      break;
    case 0x16:    // JTF address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( timerOverflow() ) goto label_%03X;\n", tmp16 );
      break;
    case 0x76:    // JF0 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( flagF0 ) goto label_%03X;\n", tmp16 );
      break;
    case 0xB6:    // JF1 address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( flagF1 ) goto label_%03X;\n", tmp16 );
      break;
    case 0x86:    // JOBF address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( outputBuffer ) goto label_%03X;\n", tmp16 );
      break;
    case 0xD6:    // JNIBF address
      tmp16 = ((progCnt) & 0b00000000) + (uint16_t)data[1];
      fprintf(outFile, "if ( inputBuffer == false ) goto label_%03X;\n", tmp16 );
      break;
      
      
    case 0x17:    // INC A
      fprintf(outFile, "accumulator++;\n");
      break;
    case 0x10:    // INC @R0
      fprintf(outFile, "RAM[R0]++;\n");
      break;
    case 0x11:    // INC @R1
      fprintf(outFile, "RAM[R1]++;\n");
      break;
    case 0x18:    // INC R0
    case 0x19:    // INC R1
    case 0x1A:    // INC R2
    case 0x1B:    // INC R3
    case 0x1C:    // INC R4
    case 0x1D:    // INC R5
    case 0x1E:    // INC R6
    case 0x1F:    // INC R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "R%01X++;\n", tmp8);
      break;
    case 0x07:    // DEC A
      fprintf(outFile, "accumulator--;\n");
      break;
    case 0xC8:    // DEC R0
    case 0xC9:    // DEC R1
    case 0xCA:    // DEC R2
    case 0xCB:    // DEC R3
    case 0xCC:    // DEC R4
    case 0xCD:    // DEC R5
    case 0xCE:    // DEC R6
    case 0xCF:    // DEC R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "R%01X--;\n", tmp8);
      break;
    case 0x03:    // ADD a, #data
      fprintf(outFile, "// Warning: ADD a, #data, flags not fully implemented\n");
      fprintf(outFile, "tmp16 = (uint16_t)accumulator + 0x%02X;\n", data[1]);
      fprintf(outFile, "accumulator = (uint8_t)(tmp16 & 0xFF);\n");
      fprintf(outFile, "if (tmp16 > 0xFF) carry = true;\n");
      break;
    case 0x60:    // ADD a, @R0
      fprintf(outFile, "// Warning: ADD a, @R0, flags not fully implemented\n");
      fprintf(outFile, "tmp16 = (uint16_t)accumulator + (uint16_t)RAM[R0];\n");
      fprintf(outFile, "accumulator = (uint8_t)(tmp16 & 0xFF);\n");
      fprintf(outFile, "if (tmp16 > 0xFF) carry = true;\n");
      break;
    case 0x61:    // ADD a, @R1
      fprintf(outFile, "// Warning: ADD a, @R1, flags not fully implemented\n");
      fprintf(outFile, "tmp16 = (uint16_t)accumulator + (uint16_t)RAM[R1];\n");
      fprintf(outFile, "accumulator = (uint8_t)(tmp16 & 0xFF);\n");
      fprintf(outFile, "if (tmp16 > 0xFF) carry = true;\n");
      break;
    case 0x68:    // ADD a, R0
    case 0x69:    // ADD a, R1
    case 0x6A:    // ADD a, R2
    case 0x6B:    // ADD a, R3
    case 0x6C:    // ADD a, R4
    case 0x6D:    // ADD a, R5
    case 0x6E:    // ADD a, R6
    case 0x6F:    // ADD a, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "// Warning: ADD a, Rn, flags not fully implemented\n");
      fprintf(outFile, "tmp16 = (uint16_t)accumulator + (uint16_t)R%01X;\n", tmp8);
      fprintf(outFile, "accumulator = (uint8_t)(tmp16 & 0xFF);\n");
      fprintf(outFile, "if (tmp16 > 0xFF) carry = true;\n");
      break;
    case 0x13:    // ADDC A, #data
      fprintf(outFile, "// Warning: ADDC a, #data, flags not fully implemented\n");
      fprintf(outFile, "accumulator += carry ? 1 : 0;\t// carry affected!\n");
      fprintf(outFile, "accumulator += ");
      fprintf(outFile, "0x%02X;\t// carry affected!\n", data[1]);
      break;
    case 0x70:    // ADDC A, @R0
      fprintf(outFile, "// Warning: ADDC a, @R0, flags not fully implemented\n");
      fprintf(outFile, "accumulator += carry ? 1 : 0;\t// carry affected!\n");
      fprintf(outFile, "accumulator += ");
      fprintf(outFile, "RAM[R0];\t// carry affected!\n");
      break;
    case 0x71:    // ADDC A, @R1
      fprintf(outFile, "// Warning: ADDC a, @R1, flags not fully implemented\n");
      fprintf(outFile, "accumulator += carry ? 1 : 0;\t// carry affected!\n");
      fprintf(outFile, "accumulator += ");
      fprintf(outFile, "RAM[R1];\t// carry affected!\n");
      break;
    case 0x78:    // ADDC a, R0
    case 0x79:    // ADDC a, R1
    case 0x7A:    // ADDC a, R2
    case 0x7B:    // ADDC a, R3
    case 0x7C:    // ADDC a, R4
    case 0x7D:    // ADDC a, R5
    case 0x7E:    // ADDC a, R6
    case 0x7F:    // ADDC a, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "// Warning: ADDC a, Rr, flags not fully implemented\n");
      fprintf(outFile, "accumulator += carry ? 1 : 0;\t// carry affected!\n");
      fprintf(outFile, "accumulator += ");
      fprintf(outFile, "R%01X;\t// carry affected!\n", tmp8);
      break;
    case 0x57:    // DA A
      fprintf(outFile, "// Warining: decimal adjust A\t// not implemented\n");
      break;

    case 0x43:    // ORL A, #data
      fprintf(outFile, "accumulator |= 0x%02X;\n", data[1]);
      break;
    case 0x40:    // ORL A, @R0
      fprintf(outFile, "accumulator |= RAM[R0];\n");
      break;
    case 0x41:    // ORL A, @R1
      fprintf(outFile, "accumulator |= RAM[R1];\n");
      break;
    case 0x48:    // ORL A, R0
    case 0x49:    // ORL A, R1
    case 0x4A:    // ORL A, R2
    case 0x4B:    // ORL A, R3
    case 0x4C:    // ORL A, R4
    case 0x4D:    // ORL A, R5
    case 0x4E:    // ORL A, R6
    case 0x4F:    // ORL A, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "accumulator |= R%01X;\n", tmp8);
      break;
    case 0x53:    // ANL A, #data
      fprintf(outFile, "accumulator &= 0x%02X;\n", data[1]);
      break;
    case 0x50:    // ANL A, @R0
      fprintf(outFile, "accumulator &= RAM[R0];\n");
      break;
    case 0x51:    // ANL A, @R1
      fprintf(outFile, "accumulator &= RAM[R1];\n");
      break;
    case 0x58:    // ANL A, R0
    case 0x59:    // ANL A, R1
    case 0x5A:    // ANL A, R2
    case 0x5B:    // ANL A, R3
    case 0x5C:    // ANL A, R4
    case 0x5D:    // ANL A, R5
    case 0x5E:    // ANL A, R6
    case 0x5F:    // ANL A, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "accumulator &= R%01X;\n", tmp8);
      break;
    case 0xD3:    // XLR A, #data
      fprintf(outFile, "accumulator ^= 0x%02X;\n", data[1]);
      break;
    case 0xD0:    // XLR A, @R0
      fprintf(outFile, "accumulator ^= RAM[R0];\n");
      break;
    case 0xD1:    // XLR A, @R1
      fprintf(outFile, "accumulator ^= RAM[R1];\n");
      break;
    case 0xD8:    // XLR A, R0
    case 0xD9:    // XLR A, R1
    case 0xDA:    // XLR A, R2
    case 0xDB:    // XLR A, R3
    case 0xDC:    // XLR A, R4
    case 0xDD:    // XLR A, R5
    case 0xDE:    // XLR A, R6
    case 0xDF:    // XLR A, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "accumulator ^= R%01X;\n", tmp8);
      break;
    case 0x20:    // XCH A, @R0
      fprintf(outFile, "tmp8 = RAM[R0];\n");
      fprintf(outFile, "RAM[R0] = accumulator;\n");
      fprintf(outFile, "accumulator = tmp8;\n");
      break;
    case 0x21:    // XCH A, @R1
      fprintf(outFile, "tmp8 = RAM[R1];\n");
      fprintf(outFile, "RAM[R1] = accumulator;\n");
      fprintf(outFile, "accumulator = tmp8;\n");
      break;
    case 0x28:    // XCH A, R0
    case 0x29:    // XCH A, R1
    case 0x2A:    // XCH A, R2
    case 0x2B:    // XCH A, R3
    case 0x2C:    // XCH A, R4
    case 0x2D:    // XCH A, R5
    case 0x2E:    // XCH A, R6
    case 0x2F:    // XCH A, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "tmp8 = R%01X;\n", tmp8);
      fprintf(outFile, "R%01X = accumulator;\n", tmp8);
      fprintf(outFile, "accumulator = tmp8;\n");
      break;
    case 0x30:
    case 0x31:
      tmp8 = data[0] & 0b00000011;
      fprintf(outFile, "tmp = accumulator;\n");
      fprintf(outFile, "accumulator = (accumulator & 0xF0) | (RAM[R%01X] & 0x0F);\n", tmp8);
      fprintf(outFile, "RAM[R%01X] = (RAM[R%01X] & 0xF0) | (tmp & 0x0F);\n", tmp8, tmp8);
      break;
    case 0xE3:     //MOVP3 A,@A
      fprintf(outFile, "accumulator = page3Array[accumulator];\t// Double check for correct array name!\n");
      break;
    case 0xA3:     //MOVP A,@A
      tmp16 = ((progCnt) & 0b00000000);
      fprintf(outFile, "accumulator = prog[0x%03X + accumulator];\t// Double check for correct address!\n", tmp16);
      break;
    case 0x23:    // MOV A, #data
      fprintf(outFile, "accumulator = 0x%02X;\n", data[1]);
      break;
    case 0xB0:    // MOV @R0, #data
      fprintf(outFile, "RAM[R0] = 0x%02X;\n", data[1]);
      break;
    case 0xB1:    // MOV @R1, #data
      fprintf(outFile, "RAM[R1] = 0x%02X;\n", data[1]);
      break;
    case 0xB8:    // MOV R0, #data
    case 0xB9:    // MOV R1, #data
    case 0xBA:    // MOV R2, #data
    case 0xBB:    // MOV R3, #data
    case 0xBC:    // MOV R4, #data
    case 0xBD:    // MOV R5, #data
    case 0xBE:    // MOV R6, #data
    case 0xBF:    // MOV R7, #data
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "R%01X = 0x%02X;\n", tmp8, data[1]);
      break;
    case 0xF0:    // MOV A, @R0
      fprintf(outFile, "accumulator = RAM[R0];\n");
      break;
    case 0xF1:    // MOV A, @R1
      fprintf(outFile, "accumulator = RAM[R1];\n");
      break;
    case 0xF8:    // MOV A, R0
    case 0xF9:    // MOV A, R1
    case 0xFA:    // MOV A, R2
    case 0xFB:    // MOV A, R3
    case 0xFC:    // MOV A, R4
    case 0xFD:    // MOV A, R5
    case 0xFE:    // MOV A, R6
    case 0xFF:    // MOV A, R7
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "accumulator = R%01X;\n", tmp8);
      break;
    case 0xA0:    // MOV @R0, A
      fprintf(outFile, "RAM[R0] = accumulator;\n");
      break;
    case 0xA1:    // MOV @R1, A
      fprintf(outFile, "RAM[R1] = accumulator;\n");
      break;
    case 0xA8:    // MOV R0, A
    case 0xA9:    // MOV R1, A
    case 0xAA:    // MOV R2, A
    case 0xAB:    // MOV R3, A
    case 0xAC:    // MOV R4, A
    case 0xAD:    // MOV R5, A
    case 0xAE:    // MOV R6, A
    case 0xAF:    // MOV R7, A
      tmp8 = data[0] & 0b00000111;
      fprintf(outFile, "R%01X = accumulator;\n", tmp8);
      break;
    case 0xC7:    // MOV A, PSW
      fprintf(outFile, "accumulator = PSW;\n");
      break;
    case 0xD7:    // MOV PSW, A
      fprintf(outFile, "PSW = accumulator;\n");
      break;
    case 0x90:    // MOV STS, A
      fprintf(outFile, "statusWrite(accumulator);\n");
      break;

    case 0x77:    // RR A
      fprintf(outFile, "accumulator = accumulator >> 1;\n");
      break;
    case 0xE7:    // RL A
      fprintf(outFile, "accumulator = accumulator << 1;\n\n");
      break;
    case 0xF7:    // RLC A
      fprintf(outFile, "if (bitRead(accumulator, 7)) tmpCarry = true;\n");
      fprintf(outFile, "accumulator = accumulator << 1;\n");
      fprintf(outFile, "if (tmpCarry) bitSet(accumulator, 0);\n");
      fprintf(outFile, "carry = tmpCarry;\n");
      break;
    case 0x67:    // RRC A
      fprintf(outFile, "if (bitRead(accumulator, 0)) tmpCarry = true;\n");
      fprintf(outFile, "accumulator = accumulator >> 1;\n");
      fprintf(outFile, "if (tmpCarry) bitSet(accumulator, 7);\n");
      fprintf(outFile, "carry = tmpCarry;\n");
      break;

    case 0x47:    // SWAP A
      fprintf(outFile, "tmp8 = (accumulator & 0xF0) >> 4;\n");
      fprintf(outFile, "accumulator = ((accumulator & 0x0F) << 4) | tmp;\n");
      break;
    case 0x27:    // CLR A
      fprintf(outFile, "accumulator = 0;\n");
      break;
    case 0x97:    // CLR C
      fprintf(outFile, "carry = false; // clear carry\n");
      break;
    case 0x85:    // CLR F0
      fprintf(outFile, "bitClear(busStatus, F0);\n");
      break;
    case 0xA5:    // CLR F1
      fprintf(outFile, "bitClear(busStatus, F1);\n");
      break;
    case 0x37:    // CPL A
      fprintf(outFile, "accumulator = ~accumulator;\n");
      break;
    case 0x95:    // CPL F0
        fprintf(outFile, "bitTogle(busStatus, F0);\n");
        break;
    case 0xA7:    // CPL C
        fprintf(outFile, "carry = !carry;\n");
        break;
    case 0xB5:    // CPL F1
        fprintf(outFile, "bitTogle(busStatus, F1);\n");
        break;

    case 0x02:    // OUT DBB,A
        fprintf(outFile, "busWrite(accumulator);\n");
        break;
    case 0x22:    // IN A, DDB
        fprintf(outFile, "accumulator = busRead();\n");
        break;

    case 0xE5:    // EN DMA
        fprintf(outFile, "enableDMA(true);\n");
        break;
    case 0xF5:    // EN FLAGS
        fprintf(outFile, "enableIrqFlags(true);\n");
        break;
    case 0x05:    // EN I
        fprintf(outFile, "enableEXIRQ(true);\n");
        break;
    case 0x15:    // DIS I
        fprintf(outFile, "enableEXIRQ(false);\n");
        break;
    case 0x25:    // EN TCNTI
        fprintf(outFile, "enableTimerIRQ(true);\n");
        break;
    case 0x35:    // DIS TCNTI
        fprintf(outFile, "enableTimerIRQ(false);\n");
        break;
    case 0xC5:    // SEL RB0
        fprintf(outFile, "// Bank0 in use\n");
        bank = false;
        break;
    case 0xD5:    // SEL RB1
        fprintf(outFile, "// Bank1 in use\n");
        bank = true;
        break;
    case 0x42:    // MOV A, T
        fprintf(outFile, "accumualtor = readTimer();\n");
        break;
    case 0x62:    // MOV T, A
        fprintf(outFile, "setTimer(accumualtor);\n");
        break;
    case 0x55:    // STRT T
        fprintf(outFile, "startTimer(modeTimer);\n");
        break;
    case 0x45:    // STRT CNT
        fprintf(outFile, "startTimer(modeCounter);\n");
        break;
    case 0x65:    // STOP TCNT
        fprintf(outFile, "startTimer(false);\n");
        break;

    case 0x08:    // IN A, P0   // invalid port
    case 0x09:    // IN A, P1
    case 0x0A:    // IN A, P2
    case 0x0B:    // IN A, P3   // invalid port
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "accumulator = portRead(P%01X);\n", tmp8);
        bank = true;
        break;
    case 0x0C:    // MOVD A, P4
    case 0x0D:    // MOVD A, P5
    case 0x0E:    // MOVD A, P6
    case 0x0F:    // MOVD A, P7
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "accumulator = portRead(P%01X);\n", tmp8 + 4);
        bank = true;
        break;
    case 0x38:    // OUTL P0, A // invalid port
    case 0x39:    // OUTL P1, A
    case 0x3A:    // OUTL P2, A
    case 0x3B:    // OUTL P3, A // invalid port
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(accumulator, P%01X);\n", tmp8);
        bank = true;
        break;
    case 0x3C:    // MOVL P4, A
    case 0x3D:    // MOVL P5, A
    case 0x3E:    // MOVL P6, A
    case 0x3F:    // MOVL P7, A
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(accumulator, P%01X);\n", tmp8 + 4);
        bank = true;
        break;
    case 0x88:    // ORL P0, #data  // invalid port
    case 0x89:    // ORL P1, #data
    case 0x8A:    // ORL P2, #data
    case 0x8B:    // ORL P3, #data  // invalid port
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(portRead(P%01X) | 0x%02X, P%01X;\n", data[1], tmp8, tmp8);
        bank = true;
        break;
    case 0x8C:    // ORLD P4, #data
    case 0x8D:    // ORLD P5, #data
    case 0x8E:    // ORLD P6, #data
    case 0x8F:    // ORLD P7, #data
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(portRead(P%01X) | accumulator, P%01X;\n", tmp8, tmp8);
        bank = true;
        break;
    case 0x98:    // ANL P0, #data  // invalid port
    case 0x99:    // ANL P1, #data
    case 0x9A:    // ANL P2, #data
    case 0x9B:    // ANL P3, #data  // invalid port
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(portRead(P%01X) & 0x%02X, P%01X;\n", data[1], tmp8, tmp8);
        bank = true;
        break;
    case 0x9C:    // ORLD P4, #data
    case 0x9D:    // ORLD P5, #data
    case 0x9E:    // ORLD P6, #data
    case 0x9F:    // ORLD P7, #data
        tmp8 = data[0] & 0b00000011;
        fprintf(outFile, "portWrite(portRead(P%01X) & 0x%02X, P%01X;\n", data[1], tmp8, tmp8);
        bank = true;
        break;
    default:
      fprintf(outFile, "// Error: something fucked up in decompiler! Opcode: 0x%02X\n", data[0]);
      break;
  }
}

// estimate only jump/call label address usage
void partialDecompile(uint8_t data[], uint16_t progCnt) {

  uint16_t tmp16;

  switch(data[0]) {
    // 11 bit jumps
    case 0x04:    // JMP
    case 0x24:    // JMP
    case 0x44:    // JMP
    case 0x64:    // JMP
    case 0x84:    // JMP
    case 0xA4:    // JMP
    case 0xC4:    // JMP
    case 0xE4:    // JMP
      tmp16 = ((uint16_t)(data[0] & 0b11100000) << 3) + (uint16_t)data[1];
      tmp16 = ( (progCnt+1) & 0b1111100000000000) + tmp16;
      labels[tmp16] = true;
      break;
    // 10 bit jumps
    case 0x14:    // CALL
    case 0x34:    // CALL
    case 0x54:    // CALL
    case 0x74:    // CALL
      tmp16 = ((uint16_t)(data[0] & 0b01100000) << 3) + (uint16_t)data[1];
      tmp16 = ( (progCnt+1) & 0b1111110000000000) + tmp16;
      labels[tmp16] = true;
      break;
    // 8 bit jumps
    case 0x12:    // JB0 addr
    case 0x32:    // JB1 addr
    case 0x52:    // JB2 addr
    case 0x72:    // JB3 addr
    case 0x92:    // JB4 addr
    case 0xB2:    // JB5 addr
    case 0xD2:    // JB6 addr
    case 0xF2:    // JB7 addr
    case 0x16:    // JTF addr
    case 0x26:    // JNT0 addr
    case 0x36:    // JT0 addr
    case 0x56:    // JT1 addr
    case 0x46:    // JNT1 addr
    case 0xB6:    // JF0 addr
    case 0x76:    // JF1 addr
    case 0x86:    // JOBF addr
    case 0x96:    // JNZ addr
    case 0xC6:    // JZ addr
    case 0xD6:    // JNIBF addr
    case 0xE6:    // JNC addr
    case 0xF6:    // JC addr
    case 0xE8:    // DNJZ R0, addr
    case 0xE9:    // DNJZ R1, addr
    case 0xEA:    // DNJZ R2, addr
    case 0xEB:    // DNJZ R3, addr
    case 0xEC:    // DNJZ R4, addr
    case 0xED:    // DNJZ R5, addr
    case 0xEE:    // DNJZ R6, addr
    case 0xEF:    // DNJZ R7, addr
      tmp16 = ((progCnt+1) & 0b00000000) + (uint16_t)data[1];
      labels[tmp16] = true;
      break;
    default:
      break;
  }
}
