func (cpu *GbcCPU) LDrr_r(hs *byte, ls *byte, r *byte) {
	var RR types.Word = types.Word(utils.JoinBytes(*hs, *ls))
	cpu.WriteByte(RR, *r)
}
LD rr, r
Load value from register (r) into memory address located at register pair (RR)
package cpu
import (
	"fmt"
	"log"
	"github.com/djhworld/gomeboycolor/constants"
	"github.com/djhworld/gomeboycolor/mmu"
	"github.com/djhworld/gomeboycolor/timer"
	"github.com/djhworld/gomeboycolor/types"
	"github.com/djhworld/gomeboycolor/utils"
)
const NAME = "CPU"
const PREFIX = NAME + ":"
const (
	_ = iota
	C
	H
	N
	Z
)
type CurrentInstruction struct {
	*Instruction
	Operands [2]byte
}
type CPUFrame struct {
	PC                      types.Word  
	SP                      types.Word  
	R                       Registers
	InterruptsEnabled       bool
	CurrentInstruction      *CurrentInstruction
	LastInstrCycle          Clock
	PCJumped                bool
	Halted                  bool
	InterruptFlagBeforeHalt byte
}
func (cpu *GbcCPU) GetFrame() *CPUFrame {
	var frame *CPUFrame = new(CPUFrame)
	frame.PC = cpu.PC
	frame.SP = cpu.SP
	frame.R = cpu.R
	frame.InterruptsEnabled = cpu.InterruptsEnabled
	frame.CurrentInstruction = cpu.CurrentInstruction
	frame.LastInstrCycle = cpu.LastInstrCycle
	frame.PCJumped = cpu.PCJumped
	frame.Halted = cpu.Halted
	frame.InterruptFlagBeforeHalt = cpu.InterruptFlagBeforeHalt
	return frame
}
type Registers struct {
	A byte
	B byte
	C byte
	D byte
	E byte
	H byte
	L byte
	F byte  
}
func (r Registers) String() string {
	formatByte := func(b byte) string {
		if b < 0x10 {
			return fmt.Sprintf("0x0%X", b)
		}
		return fmt.Sprintf("0x%X", b)
	}
	return fmt.Sprintf("A: %s  B: %s  C: %s  D: %s  E: %s  H: %s  L: %s", formatByte(r.A), formatByte(r.B), formatByte(r.C), formatByte(r.D), formatByte(r.E), formatByte(r.H), formatByte(r.L))
}
type Clock struct {
	M int
	t int
}
func (c *Clock) T() int {
	return c.M * 4
}
func (c *Clock) Reset() {
	c.M = 0
	c.t = 0
}
func (c *Clock) String() string {
	return fmt.Sprintf("[M: %X, T: %X]", c.M, c.t)
}
type GbcCPU struct {
	PC                      types.Word  
	SP                      types.Word  
	R                       Registers
	InterruptsEnabled       bool
	CurrentInstruction      *CurrentInstruction
	LastInstrCycle          Clock
	mmu                     mmu.MemoryMappedUnit
	timer                   *timer.Timer
	PCJumped                bool
	Halted                  bool
	InterruptFlagBeforeHalt byte
	Speed                   int
}
func NewCPU(m mmu.MemoryMappedUnit, timer *timer.Timer) *GbcCPU {
	cpu := new(GbcCPU)
	cpu.Reset()
	cpu.mmu = m
	cpu.timer = timer
	log.Println(PREFIX, "Linked CPU to MMU")
	return cpu
}
func (cpu *GbcCPU) Reset() {
	log.Println(PREFIX, "Resetting", NAME)
	cpu.PC = 0
	cpu.SP = 0
	cpu.R.A = 0
	cpu.R.B = 0
	cpu.R.C = 0
	cpu.R.D = 0
	cpu.R.E = 0
	cpu.R.F = 0
	cpu.R.H = 0
	cpu.R.L = 0
	cpu.Speed = 1
	cpu.CurrentInstruction = &CurrentInstruction{Instruction: Instructions[0x00], Operands: [2]byte{}}
	cpu.InterruptsEnabled = true
	cpu.LastInstrCycle.Reset()
	cpu.PCJumped = false
	cpu.Halted = false
}
func (cpu *GbcCPU) FlagsString() string {
	var flags string = ""
	var minus string = "-"
	if cpu.IsFlagSet(Z) {
		flags += "Z"
	} else {
		flags += minus
	}
	if cpu.IsFlagSet(N) {
		flags += "N"
	} else {
		flags += minus
	}
	if cpu.IsFlagSet(H) {
		flags += "H"
	} else {
		flags += minus
	}
	if cpu.IsFlagSet(C) {
		flags += "C"
	} else {
		flags += minus
	}
	return flags
}
func (cpu *GbcCPU) String() string {
	return fmt.Sprint("PC: ", cpu.PC, "  SP: ", cpu.SP, "  ", cpu.R, "  ", cpu.FlagsString(), "  ", cpu.CurrentInstruction)
}
func (cpu *GbcCPU) ResetFlag(flag int) {
	switch flag {
	case Z:
		cpu.R.F = cpu.resetBit(7, cpu.R.F)
	case N:
		cpu.R.F = cpu.resetBit(6, cpu.R.F)
	case H:
		cpu.R.F = cpu.resetBit(5, cpu.R.F)
	case C:
		cpu.R.F = cpu.resetBit(4, cpu.R.F)
	default:
		log.Fatalf(PREFIX+" Unknown flag %c", flag)
	}
	cpu.R.F &= 0xF0
}
func (cpu *GbcCPU) SetFlag(flag int) {
	switch flag {
	case Z:
		cpu.R.F = cpu.setBit(7, cpu.R.F)
	case N:
		cpu.R.F = cpu.setBit(6, cpu.R.F)
	case H:
		cpu.R.F = cpu.setBit(5, cpu.R.F)
	case C:
		cpu.R.F = cpu.setBit(4, cpu.R.F)
	default:
		log.Fatalf(PREFIX+" Unknown flag %c", flag)
	}
	cpu.R.F &= 0xF0
}
func (cpu *GbcCPU) IsFlagSet(flag int) bool {
	switch flag {
	case Z:
		return cpu.R.F&0x80 == 0x80
	case N:
		return cpu.R.F&0x40 == 0x40
	case H:
		return cpu.R.F&0x20 == 0x20
	case C:
		return cpu.R.F&0x10 == 0x10
	default:
		log.Fatalf(PREFIX+" Unknown flag %c", flag)
	}
	return false
}
func (cpu *GbcCPU) IncrementPC(by int) {
	cpu.PC += types.Word(by)
}
func (cpu *GbcCPU) Step() int {
	cpu.LastInstrCycle.Reset()
	var opcode byte
	if !cpu.Halted {
		cpu.CheckForInterrupts()
		opcode = cpu.ReadByte(cpu.PC)
		if opcode == 0xCB {
			cpu.IncrementPC(1)
			opcode = cpu.ReadByte(cpu.PC)
			cpu.Compile(InstructionsCB[opcode])
		} else {
			cpu.Compile(Instructions[opcode])
		}
		cpu.CurrentInstruction.Execute(cpu)
		if cpu.PCJumped == false {
			cpu.IncrementPC(cpu.CurrentInstruction.OperandsSize + 1)
		}
		cpu.PCJumped = false
	} else {
		iflagnow := cpu.mmu.ReadByte(constants.INTERRUPT_FLAG_ADDR)
		if cpu.InterruptFlagBeforeHalt != iflagnow {
			cpu.Halted = false
		}
		cpu.tick(1)
	}
	return cpu.LastInstrCycle.M
}
func (cpu *GbcCPU) CheckForInterrupts() bool {
	if cpu.InterruptsEnabled {
		var ie byte = cpu.mmu.ReadByte(constants.INTERRUPT_ENABLED_FLAG_ADDR)
		var iflag byte = cpu.mmu.ReadByte(constants.INTERRUPT_FLAG_ADDR)
		var interrupt byte = iflag & ie
		if interrupt != 0x00 {
			switch {
			case interrupt&constants.V_BLANK_IRQ == constants.V_BLANK_IRQ:
				cpu.mmu.WriteByte(constants.INTERRUPT_FLAG_ADDR, iflag&0xFE)
				cpu.pushWordToStack(cpu.PC)
				cpu.PC = types.Word(constants.V_BLANK_IR_ADDR)
				cpu.InterruptsEnabled = false
				return true
			case interrupt&constants.LCD_IRQ == constants.LCD_IRQ:
				cpu.mmu.WriteByte(constants.INTERRUPT_FLAG_ADDR, iflag&0xFD)
				cpu.pushWordToStack(cpu.PC)
				cpu.PC = types.Word(constants.LCD_IR_ADDR)
				cpu.InterruptsEnabled = false
				return true
			case interrupt&constants.TIMER_OVERFLOW_IRQ == constants.TIMER_OVERFLOW_IRQ:
				cpu.mmu.WriteByte(constants.INTERRUPT_FLAG_ADDR, iflag&0xFB)
				cpu.pushWordToStack(cpu.PC)
				cpu.PC = types.Word(constants.TIMER_OVERFLOW_IR_ADDR)
				cpu.InterruptsEnabled = false
				return true
			case interrupt&constants.JOYP_HILO_IRQ == constants.JOYP_HILO_IRQ:
				log.Println("JOYP!")
				cpu.mmu.WriteByte(constants.INTERRUPT_FLAG_ADDR, iflag&0xEF)
				cpu.pushWordToStack(cpu.PC)
				cpu.PC = types.Word(constants.JOYP_HILO_IR_ADDR)
				cpu.InterruptsEnabled = false
				return true
			default:
				log.Fatalf("Unknown interrupt = %d", interrupt)
			}
			cpu.tick(1)
		}
	}
	return false
}
func (cpu *GbcCPU) SetCPUSpeed() {
	var speedPrepRegister byte = cpu.mmu.ReadByte(mmu.CGB_DOUBLE_SPEED_PREP_REG)
	if speedPrepRegister&0x01 == 0x01 {
		switch cpu.Speed {
		case 2:
			cpu.Speed = 1
			cpu.mmu.WriteByte(mmu.CGB_DOUBLE_SPEED_PREP_REG, 0x00)
		case 1:
			cpu.Speed = 2
			cpu.mmu.WriteByte(mmu.CGB_DOUBLE_SPEED_PREP_REG, 0x80)
		default:
			panic(fmt.Sprint("Unsupported CPU speed ", cpu.Speed, " this should not happen!"))
		}
		log.Printf("CPU: Setting CPU speed to %dx speed", cpu.Speed)
	}
}
func (cpu *GbcCPU) Compile(instruction *Instruction) {
	cpu.CurrentInstruction.Instruction = instruction
	switch instruction.OperandsSize {
	case 1:
		cpu.CurrentInstruction.Operands[0] = cpu.ReadByte(cpu.PC + 1)
	case 2:
		cpu.CurrentInstruction.Operands[0] = cpu.ReadByte(cpu.PC + 1)
		cpu.CurrentInstruction.Operands[1] = cpu.ReadByte(cpu.PC + 2)
	}
}
func (cpu *GbcCPU) pushByteToStack(b byte) {
	cpu.SP--
	cpu.WriteByte(cpu.SP, b)
}
func (cpu *GbcCPU) pushWordToStack(word types.Word) {
	hs, ls := utils.SplitIntoBytes(uint16(word))
	cpu.pushByteToStack(hs)
	cpu.pushByteToStack(ls)
}
func (cpu *GbcCPU) popByteFromStack() byte {
	var b byte = cpu.ReadByte(cpu.SP)
	cpu.SP++
	return b
}
func (cpu *GbcCPU) popWordFromStack() types.Word {
	ls := cpu.popByteFromStack()
	hs := cpu.popByteFromStack()
	return types.Word(utils.JoinBytes(hs, ls))
}
func (cpu *GbcCPU) ReadByte(addr types.Word) byte {
	cpu.tick(1)
	return cpu.mmu.ReadByte(addr)
}
func (cpu *GbcCPU) WriteByte(addr types.Word, value byte) {
	cpu.tick(1)
	cpu.mmu.WriteByte(addr, value)
}
func (cpu *GbcCPU) addBytes(a, b byte) byte {
	var calculation byte = a + b
	cpu.ResetFlag(N)
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	if (calculation^b^a)&0x10 == 0x10 {
		cpu.SetFlag(H)
	} else {
		cpu.ResetFlag(H)
	}
	if calculation < a {
		cpu.SetFlag(C)
	} else {
		cpu.ResetFlag(C)
	}
	return calculation
}
func (cpu *GbcCPU) addWords(a, b types.Word) types.Word {
	var calculation types.Word = a + b
	if calculation < a {
		cpu.SetFlag(C)
	} else {
		cpu.ResetFlag(C)
	}
	if (calculation^b^a)&0x1000 == 0x1000 {
		cpu.SetFlag(H)
	} else {
		cpu.ResetFlag(H)
	}
	cpu.ResetFlag(N)
	return calculation
}
func (cpu *GbcCPU) subBytes(a, b byte) byte {
	cpu.SetFlag(N)
	if (int(a) & 0xF) < (int(b) & 0xF) {
		cpu.SetFlag(H)
	} else {
		cpu.ResetFlag(H)
	}
	if (int(a) & 0xFF) < (int(b) & 0xFF) {
		cpu.SetFlag(C)
	} else {
		cpu.ResetFlag(C)
	}
	var calculation byte = a - b
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	return calculation
}
func (cpu *GbcCPU) andBytes(a, b byte) byte {
	var calculation byte = a & b
	cpu.SetFlag(H)
	cpu.ResetFlag(N)
	cpu.ResetFlag(C)
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	return calculation
}
func (cpu *GbcCPU) orBytes(a, b byte) byte {
	var calculation byte = a | b
	cpu.ResetFlag(H)
	cpu.ResetFlag(N)
	cpu.ResetFlag(C)
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	return calculation
}
func (cpu *GbcCPU) xorBytes(a, b byte) byte {
	var calculation byte = a ^ b
	cpu.ResetFlag(H)
	cpu.ResetFlag(N)
	cpu.ResetFlag(C)
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	return calculation
}
func (cpu *GbcCPU) incByte(value byte) byte {
	var calculation byte = value + 0x01
	cpu.ResetFlag(N)
	if calculation == 0 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	if (calculation^0x01^value)&0x10 == 0x10 {
		cpu.SetFlag(H)
	} else {
		cpu.ResetFlag(H)
	}
	return calculation
}
func (cpu *GbcCPU) decByte(a byte) byte {
	var calculation byte = a - 1
	cpu.SetFlag(N)
	if calculation == 0 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	if (calculation^0x01^a)&0x10 == 0x10 {
		cpu.SetFlag(H)
	} else {
		cpu.ResetFlag(H)
	}
	return calculation
}
func (cpu *GbcCPU) swapByte(a byte) byte {
	var calculation byte = utils.SwapNibbles(a)
	cpu.ResetFlag(N)
	cpu.ResetFlag(H)
	cpu.ResetFlag(C)
	if calculation == 0x00 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	return calculation
}
func (cpu *GbcCPU) bitTest(bit byte, a byte) {
	if (a >> bit & 1) == 0 {
		cpu.SetFlag(Z)
	} else {
		cpu.ResetFlag(Z)
	}
	cpu.ResetFlag(N)
	cpu.SetFlag(H)
}
func (cpu *GbcCPU) setBit(bit byte, a byte) byte {
	return a | (1 << uint(bit))
}
func (cpu *GbcCPU) resetBit(bit byte, a byte) byte {
	return a & ^(1 << uint(bit))
}
func (cpu *GbcCPU) NOP() {
}
func (cpu *GbcCPU) HALT() {
	cpu.InterruptFlagBeforeHalt = cpu.mmu.ReadByte(constants.INTERRUPT_FLAG_ADDR)
	cpu.Halted = true
}
func (cpu *GbcCPU) Stop() {
	log.Println("CPU: Stopping...")
	cpu.SetCPUSpeed()
}
func (cpu *GbcCPU) DI() {
	cpu.InterruptsEnabled = false
}
func (cpu *GbcCPU) EI() {
	cpu.InterruptsEnabled = true
}
func (cpu *GbcCPU) LDrn(r *byte) {
	*r = cpu.CurrentInstruction.Operands[0]
}
func (cpu *GbcCPU) LDrr(r1 *byte, r2 *byte) {
	*r1 = *r2
}