const expect = require('chai').expect;
const Assembler = require('../src/chip/MOS6502/assembler.js');


describe("Testing MOS6502 Assembler...", function(){
  var asm = new Assembler();

  it(".reset()", function(){
    asm.compile("ADC $44");
    asm.reset();
    expect(asm.PC).to.equal(0);
    expect(Object.keys(asm.variables).length).to.equal(0);
    expect(Object.keys(asm.jumplabels).length).to.equal(0);
    expect(asm.result().length).to.equal(0);
  });

  it(".result()", function(){
    asm.reset();
    asm.compile("ADC $44");
    expect(asm.result().length).to.be.gt(0);
  });

  describe(".compile()", function(){
    it(".define directive", function(){
      asm.compile(".define TAG $44");
      let vars = asm.variables;
      expect(vars).to.have.key('TAG');
      expect(vars['TAG']).to.equal(0x44);
    });

    it(".def directive (.define shorthand)", function(){
      asm.compile(".define TAG2 $55");
      let vars = asm.variables;
      expect(vars['TAG2']).to.exist;
      expect(vars['TAG2']).to.equal(0x55);
    });

    it("Jump labels", function(){
      let pc = asm.PC;
      asm.compile("loop:\nBNE loop");
      let jmp = asm.jumplabels;
      expect(jmp).to.have.key('loop');
      expect(jmp['loop']).to.equal(pc);
    });

    it(".bytes directive", function(){
      let res = asm.reset().compile(".BYTES $44, $55, $66, $77, b10110101, 128").result();
      expect(res.length).to.equal(6);
      expect(res[0]).to.equal(0x44);
      expect(res[1]).to.equal(0x55);
      expect(res[2]).to.equal(0x66);
      expect(res[3]).to.equal(0x77);
      expect(res[4]).to.equal(parseInt('10110101', 2));
      expect(res[5]).to.equal(128);
    });

    it(".byt directive (.bytes shorthand)", function(){
      let res = asm.reset().compile(".BYT $44, $55, $66").result();
      expect(res.length).to.equal(3);
      expect(res[0]).to.equal(0x44);
      expect(res[1]).to.equal(0x55);
      expect(res[2]).to.equal(0x66);
    });

    it("Jump label before and after .byte directive", function(){
      asm.reset();
      let pc1 = asm.PC;
      asm.compile("bytes: .bytes $44, $55, $66, $77");
      let pc2 = asm.PC;
      asm.compile("loop: BNE loop");

      let jmp = asm.jumplabels;
      //expect(jmp).to.have.key('bytes');
      expect(jmp['bytes']).to.equal(pc1);
      //expect(jmp).to.have.key('loop');
      expect(jmp['loop']).to.equal(pc2);

      let res = asm.result();
      expect(res.length).to.equal(6);
      expect(res[0]).to.equal(0x44);
      expect(res[1]).to.equal(0x55);
      expect(res[2]).to.equal(0x66);
      expect(res[3]).to.equal(0x77);
      expect(res[4]).to.equal(0xD0);
      expect(res[5]).to.equal(0xFE);
    });

    it("ADC Immediate", function(){
      asm.reset();
      asm.compile("ADC #$44");
      let res = asm.result();
      expect(res.length).to.equal(2);
      expect(res[0]).to.equal(0x69);
      expect(res[1]).to.equal(0x44);
    });

    it("ADC Zero Page", function(){
      asm.reset();
      asm.compile("ADC $44");
      let res = asm.result();
      expect(res.length).to.equal(2);
      expect(res[0]).to.equal(0x65);
      expect(res[1]).to.equal(0x44);
    });

    it("ADC Zero Page, X", function(){
      asm.reset();
      asm.compile("ADC $44, X");
      let res = asm.result();
      expect(res.length).to.equal(2);
      expect(res[0]).to.equal(0x75);
      expect(res[1]).to.equal(0x44);
    });

    it("ADC Absolute", function(){
      asm.reset();
      asm.compile("ADC $4400");
      let res = asm.result();
      expect(res.length).to.equal(3);
      expect(res[0]).to.equal(0x6D);
      expect(res[1]).to.equal(0x00);
      expect(res[2]).to.equal(0x44);
    });

    it("ADC Absolute, X", function(){
      asm.reset();
      asm.compile("ADC $4400,X");
      let res = asm.result();
      expect(res.length).to.equal(3);
      expect(res[0]).to.equal(0x7D);
      expect(res[1]).to.equal(0x00);
      expect(res[2]).to.equal(0x44);
    });

    it("ADC Absolute, Y", function(){
      asm.reset();
      asm.compile("ADC $4400, Y");
      let res = asm.result();
      expect(res.length).to.equal(3);
      expect(res[0]).to.equal(0x79);
      expect(res[1]).to.equal(0x00);
      expect(res[2]).to.equal(0x44);
    });

    it("ADC Indirect, X", function(){
      asm.reset();
      asm.compile("ADC ($44,X)");
      let res = asm.result();
      expect(res.length).to.equal(2);
      expect(res[0]).to.equal(0x61);
      expect(res[1]).to.equal(0x44);
    });

    it("ADC Indirect, Y", function(){
      asm.reset();
      asm.compile("ADC ($44), Y");
      let res = asm.result();
      expect(res.length).to.equal(2);
      expect(res[0]).to.equal(0x71);
      expect(res[1]).to.equal(0x44);
    });
    // -------------------------------------------------------------
    it("AND Immediate");
    it("AND Zero Page");
    it("AND Zero Page, X");
    it("AND Absolute");
    it("AND Absolute, X");
    it("AND Absolute, Y");
    it("AND Indirect, X");
    it("AND Indirect, Y");
    // -------------------------------------------------------------
    it("ASL Accumulator");
    it("ASL Zero Page");
    it("ASL Zero Page, X");
    it("ASL Absolute");
    it("ASL Absolute, X");
    // -------------------------------------------------------------
    it("BIT Zero Page");
    it("BIT Absolute");
    // -------------------------------------------------------------
    it("BPL");
    it("BMI");
    it("BVC");
    it("BVS");
    it("BCC");
    it("BCS");
    it("BNE");
    it("BEQ");
    // -------------------------------------------------------------
    it ("BRK", function(){
      asm.reset();
      let res = asm.compile("BRK").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x00);
    });
    // -------------------------------------------------------------
    it("CMP Immediate");
    it("CMP Zero Page");
    it("CMP Zero Page, X");
    it("CMP Absolute");
    it("CMP Absolute, X");
    it("CMP Absolute, Y");
    it("CMP Indirect, X");
    it("CMP Indirect, Y");
    // -------------------------------------------------------------
    it("CPX Immediate");
    it("CPX Zero Page");
    it("CPX Absolute");
    // -------------------------------------------------------------
    it("CPY Immediate");
    it("CPY Zero Page");
    it("CPY Absolute");
    // -------------------------------------------------------------
    it("DEC Zero Page");
    it("DEC Zero Page, X");
    it("DEC Absolute");
    it("DEC Absolute, X");

    // -------------------------------------------------------------
    it("EOR Immediate");
    it("EOR Zero Page");
    it("EOR Zero Page, X");
    it("EOR Absolute");
    it("EOR Absolute, X");
    it("EOR Absolute, Y");
    it("EOR Indirect, X");
    it("EOR Indirect, Y");

    // -------------------------------------------------------------
    it("CLC", function(){
      asm.reset();
      let res = asm.compile("CLC").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x18);
    });

    it("CLD", function(){
      asm.reset();
      let res = asm.compile("CLD").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xD8);
    });

    it("CLI", function(){
      asm.reset();
      let res = asm.compile("CLI").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x58);
    });

    it("CLV", function(){
      asm.reset();
      let res = asm.compile("CLV").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xB8);
    });

    it("SEC", function(){
      asm.reset();
      let res = asm.compile("SEC").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x38);
    });

    it("SED", function(){
      asm.reset();
      let res = asm.compile("SED").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xF8);
    });

    it("SEI", function(){
      asm.reset();
      let res = asm.compile("SEI").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x78);
    });

    // -------------------------------------------------------------
    it("INC Zero Page");
    it("INC Zero Page, X");
    it("INC Absolute");
    it("INC Absolute, X");
    // -------------------------------------------------------------
    it("JMP Absolute");
    it("JMP Indirect");
    // -------------------------------------------------------------
    it("JRS Absolute");
    // -------------------------------------------------------------
    it("LDA Immediate");
    it("LDA Zero Page");
    it("LDA Zero Page, X");
    it("LDA Absolute");
    it("LDA Absolute, X");
    it("LDA Absolute, Y");
    it("LDA Indirect, X");
    it("LDA Indirect, Y");
    // -------------------------------------------------------------
    it("LDX Immediate");
    it("LDX Zero Page");
    it("LDX Zero Page, Y");
    it("LDX Absolute");
    it("LDX Absolute, Y");
    // -------------------------------------------------------------
    it("LDY Immediate");
    it("LDY Zero Page");
    it("LDY Zero Page, X");
    it("LDY Absolute");
    it("LDY Absolute, X");
    // -------------------------------------------------------------
    it("LSR Accumulator");
    it("LSR Zero Page");
    it("LSR Zero Page, X");
    it("LSR Absolute");
    it("LSR Absolute, X");
    // -------------------------------------------------------------
    it("NOP", function(){
      asm.reset();
      let res = asm.compile("NOP").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xEA);
    });

    // -------------------------------------------------------------
    it("ORA Immediate");
    it("ORA Zero Page");
    it("ORA Zero Page, X");
    it("ORA Absolute");
    it("ORA Absolute, X");
    it("ORA Absolute, Y");
    it("ORA Indirect, X");
    it("ORA Indirect, Y");
    // -------------------------------------------------------------
    it("DEX", function(){
      asm.reset();
      let res = asm.compile("DEX").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xCA);
    });

    it("DEY", function(){
      asm.reset();
      let res = asm.compile("DEY").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x88);
    });

    it("INX", function(){
      asm.reset();
      let res = asm.compile("INX").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xE8);
    });

    it("INY", function(){
      asm.reset();
      let res = asm.compile("INY").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xC8);
    });

    it("TAX", function(){
      asm.reset();
      let res = asm.compile("TAX").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xAA);
    });

    it("TAY", function(){
      asm.reset();
      let res = asm.compile("TAY").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xA8);
    });

    it("TXA", function(){
      asm.reset();
      let res = asm.compile("TXA").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x8A);
    });

    it("TYA", function(){
      asm.reset();
      let res = asm.compile("TYA").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x98);
    });

    // -------------------------------------------------------------
    it("ROL Accumulator");
    it("ROL Zero Page");
    it("ROL Zero Page, X");
    it("ROL Absolute");
    it("ROL Absolute, X");
    // -------------------------------------------------------------
    it("ROR Accumulator");
    it("ROR Zero Page");
    it("ROR Zero Page, X");
    it("ROR Absolute");
    it("ROR Absolute, X");
    // -------------------------------------------------------------
    it("RTI", function(){
      asm.reset();
      let res = asm.compile("RTI").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x40);
    });
    it("RTS", function(){
      asm.reset();
      let res = asm.compile("RTS").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x60);
    });

    // -------------------------------------------------------------
    it("SBC Immediate");
    it("SBC Zero Page");
    it("SBC Zero Page, X");
    it("SBC Absolute");
    it("SBC Absolute, X");
    it("SBC Absolute, Y");
    it("SBC Indirect, X");
    it("SBC Indirect, Y");
    // -------------------------------------------------------------
    it("STA Zero Page");
    it("STA Zero Page, X");
    it("STA Absolute");
    it("STA Absolute, X");
    it("STA Absolute, Y");
    it("STA Indirect, X");
    it("STA Indirect, Y");
    // -------------------------------------------------------------
    it("PHA", function(){
      asm.reset();
      let res = asm.compile("PHA").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x48);
    });

    it("PHP", function(){
      asm.reset();
      let res = asm.compile("PHP").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x08);
    });

    it("PLA", function(){
      asm.reset();
      let res = asm.compile("PLA").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x68);
    });

    it("PLP", function(){
      asm.reset();
      let res = asm.compile("PLP").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x28);
    });

    it("TSX", function(){
      asm.reset();
      let res = asm.compile("TSX").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0xBA);
    });

    it("TXS", function(){
      asm.reset();
      let res = asm.compile("TXS").result();
      expect(res.length).to.equal(1);
      expect(res[0]).to.equal(0x9A);
    });

    // -------------------------------------------------------------
    it("STX Zero Page");
    it("STX Zero Page, Y");
    it("STX Absolute");
    // -------------------------------------------------------------
    it("STY Zero Page");
    it("STY Zero Page, X");
    it("STY Absolute");

    // ------------------------------------------------------------- 
  });
});