package c99

import log "github.com/sirupsen/logrus"

// executes a CB opcode
func (z *Z80) execOpcodeCB(opcode byte) {
	z.cycleCount += 8
	z.incR()

	// decoding instructions from http://z80.info/decoding.htm#cb
	x_ := (opcode >> 6) & 3 // 0b11
	y_ := (opcode >> 3) & 7 // 0b111
	z_ := opcode & 7        // 0b111

	var hl byte
	v := byte(0)
	reg := &v
	switch z_ {
	case 0:
		reg = &z.b
	case 1:
		reg = &z.c
	case 2:
		reg = &z.d
	case 3:
		reg = &z.e
	case 4:
		reg = &z.h
	case 5:
		reg = &z.l
	case 6:
		hl = z.rb(z.hl())
		reg = &hl
	case 7:
		reg = &z.a
	}

	switch x_ {
	case 0:
		// rot[y] r[z]
		switch y_ {
		case 0:
			*reg = z.cbRlc(*reg)
		case 1:
			*reg = z.cbRrc(*reg)
		case 2:
			*reg = z.cbRl(*reg)
		case 3:
			*reg = z.cbRr(*reg)
		case 4:
			*reg = z.cbSla(*reg)
		case 5:
			*reg = z.cbSra(*reg)
		case 6:
			*reg = z.cbSll(*reg)
		case 7:
			*reg = z.cbSrl(*reg)
		}

	case 1:
		// BIT y, r[z]
		z.cbBit(*reg, y_)

		// in bit (hl), x/y flags are handled differently:
		if z_ == 6 {
			z.updateXY(byte(z.memPtr >> 8))
			z.cycleCount += 4
		}

	case 2:
		*reg &= ^(1 << y_) // RES y, r[z]
	case 3:
		*reg |= 1 << y_ // SET y, r[z]
	}

	if (x_ == 0 || x_ == 2 || x_ == 3) && z_ == 6 {
		z.cycleCount += 7
	}

	if reg == &hl {
		z.wb(z.hl(), hl)
	}
}

// execOpcodeDcb executes a displaced CB opcode (DDCB or FDCB)
func (z *Z80) execOpcodeDcb(opcode byte, addr uint16) {
	val := z.rb(addr)
	result := byte(0)

	// decoding instructions from http://z80.info/decoding.htm#ddcb
	x_ := (opcode >> 6) & 3 // 0b11
	y_ := (opcode >> 3) & 7 // 0b111
	z_ := opcode & 7        // 0b111

	switch x_ {
	case 0:
		// rot[y] (iz+d)
		switch y_ {
		case 0:
			result = z.cbRlc(val)
		case 1:
			result = z.cbRrc(val)
		case 2:
			result = z.cbRl(val)
		case 3:
			result = z.cbRr(val)
		case 4:
			result = z.cbSla(val)
		case 5:
			result = z.cbSra(val)
		case 6:
			result = z.cbSll(val)
		case 7:
			result = z.cbSrl(val)
		}

	case 1:
		// bit y,(iz+d)
		result = z.cbBit(val, y_)
		z.updateXY(byte(addr >> 8))
	case 2:
		result = val & ^(1 << y_) // res y, (iz+d)
	case 3:
		result = val | (1 << y_) // set y, (iz+d)

	default:
		log.Errorf("Unknown XYCB opcode: %02X\n", opcode)
	}

	// ld r[z], rot[y] (iz+d)
	// ld r[z], res y,(iz+d)
	// ld r[z], set y,(iz+d)
	if x_ != 1 && z_ != 6 {
		switch z_ {
		case 0:
			z.b = result
		case 1:
			z.c = result
		case 2:
			z.d = result
		case 3:
			z.e = result
		case 4:
			z.h = result
		case 5:
			z.l = result
		// always false
		//case 6:
		//	z.wb(z.hl(), result)
		case 7:
			z.a = result
		}
	}

	if x_ == 1 {
		// bit instructions take 20 cycles, others take 23
		z.cycleCount += 20
	} else {
		z.wb(addr, result)
		z.cycleCount += 23
	}
}
