/*
 * HalfNES by Andrew Hoffman
 * Licensed under the GNU GPL Version 3. See LICENSE file
 */
package com.grapeshot.halfnes.mappers;

import com.grapeshot.halfnes.*;

public class VRC3Mapper extends Mapper {

    private int irqctr, irqreload = 0;
    private boolean irqmode, irqenable, irqackenable, interrupted = false;

    @Override
    public void loadrom() throws BadMapperException {
        // needs to be in every mapper. Fill with initial cfg
        super.loadrom();
        //swappable bank
        for (int i = 0; i < 16; ++i) {
            prg_map[i] = (1024 * i) & (prgsize - 1);
        }
        //fixed bank
        for (int i = 1; i <= 16; ++i) {
            prg_map[32 - i] = prgsize - (1024 * i);
        }
        for (int i = 0; i < 8; ++i) {
            chr_map[i] = (1024 * i) & (chrsize - 1);
        }
    }

    @Override
    public final void cartWrite(int addr, int data) {
        if (addr < 0x8000 || addr > 0xffff) {
            super.cartWrite(addr, data);
            return;
        }

        switch (addr >> 12) {
            case 0x8:   //Bits 0-3 of IRQ reload value
                irqreload = (irqreload & 0xFFF0) | (data & 0xF);
                break;
            case 0x9:   //Bits 4-7 of IRQ reload value
                irqreload = (irqreload & 0xFF0F) | (data & 0xF) << 4;
                break;
            case 0xA:   //Bits 8-11 of IRQ reload value
                irqreload = (irqreload & 0xF0FF) | (data & 0xF) << 8;
                break;
            case 0xB:   //Bits 12-15 of IRQ reload value
                irqreload = (irqreload & 0x0FFF) | (data & 0xF) << 12;
                break;
            case 0xC:   //IRQ Control
                irqmode = ((data & (utils.BIT2)) != 0);
                irqackenable = ((data & (utils.BIT0)) != 0);

                irqenable = ((data & (utils.BIT1)) != 0);
                if (irqenable) {
                    if (irqmode) {
                        irqctr &= 0xFF00;
                        irqctr |= (irqreload & 0xFF);
                    } else {
                        irqctr = irqreload;
                    }

                    if (interrupted) {
                        --cpu.interrupt;
                        interrupted = false;
                    }
                }
                break;
            case 0xD:   //IRQ Acknowledge
                irqenable = irqackenable;
                if (interrupted) {
                    --cpu.interrupt;
                    interrupted = false;
                }
                break;
            case 0xF:   //PRG Select  
                for (int i = 0; i < 16; ++i) {
                    prg_map[i] = (1024 * (i + 16 * (data & 0xF))) & (prgsize - 1);
                }
                break;
        }
    }

    @Override
    public void cpucycle(final int cycles) {
        if (irqenable) {
            if (irqmode) {  //8-bit mode
                int temp = irqctr;
                irqctr &= 0xFF00;
                if (temp >= 0xFF) {
                    irqctr = irqreload;
                    irqctr |= (irqreload & 0xFF);
                    if (!interrupted) {
                        ++cpu.interrupt;
                        interrupted = true;
                    }
                } else {
                    temp += cycles;
                    irqctr |= temp;
                }
            } else {    //16-bit mode
                if (irqctr >= 0xFFFF) {
                    irqctr = irqreload;
                    if (!interrupted) {
                        ++cpu.interrupt;
                        interrupted = true;
                    }
                } else {
                    irqctr += cycles;
                }
            }
        }
    }
}
