func (c *Cartridge) String() string {
	startingString := "Gameboy"
	if c.IsColourGB {
		startingString += " Color"
	}

	var destinationRegion string
	if c.IsJapanese {
		destinationRegion = "Japanese"
	} else {
		destinationRegion = "Non-Japanese"
	}

	var header []string = []string{
		fmt.Sprintf(utils.PadRight("Title:", 19, " ")+"%s", c.Title),
		fmt.Sprintf(utils.PadRight("Type:", 19, " ")+"%s %s", c.Type.Description, utils.ByteToString(c.Type.ID)),
		fmt.Sprintf(utils.PadRight("Destination code:", 19, " ")+"%s", destinationRegion),
		fmt.Sprintf(utils.PadRight("File:", 19, " ")+"%s", c.Filename),
	}

	return fmt.Sprintln("\n"+startingString, "Cartridge") +
		fmt.Sprintln(strings.Repeat("-", 50)) +
		fmt.Sprintln(strings.Join(header, "\n")) +
		fmt.Sprintln(c.MBC) +
		fmt.Sprintln(strings.Repeat("-", 50))
}
func (c *Cartridge) Init(rom []byte) error {
	if size := len(rom); size < 32768 {
		return errors.New(fmt.Sprintf("ROM size %d is too small", size))
	}

	c.Title = strings.TrimSpace(string(rom[0x0134:0x0142]))
	c.IsColourGB = (rom[0x0143] == 0x80) || (rom[0x0143] == 0xC0)

	ctype := rom[0x0147]
	//validate
	if v, ok := CartridgeTypes[ctype]; !ok {
		return errors.New(fmt.Sprintf("Unknown cartridge type: %X for ROM", ctype))
	} else {
		c.Type = v
	}

	if romSize := rom[0x0148]; romSize > 0x06 {
		return errors.New(fmt.Sprintf("Handling for ROM size id: 0x%X is currently unimplemented", romSize))
	} else {
		c.ROMSize = 0x8000 << romSize
	}

	switch rom[0x0149] {
	case 0x00:
		c.RAMSize = 0
	case 0x01:
		c.RAMSize = 2048
	case 0x02:
		c.RAMSize = 8192
	case 0x03:
		c.RAMSize = 32768
	case 0x04:
		c.RAMSize = 131072
	}

	c.IsJapanese = (rom[0x014A] == 0x00)

	switch c.Type.ID {
	case MBC_0:
		c.MBC = NewMBC0(rom)
	case MBC_1, MBC_1_RAM:
		c.MBC = NewMBC1(rom, c.ROMSize, c.RAMSize, false)
	case MBC_1_RAM_BATT:
		c.MBC = NewMBC1(rom, c.ROMSize, c.RAMSize, true)
	case MBC_3_RAM_BATT:
		c.MBC = NewMBC3(rom, c.ROMSize, c.RAMSize, true, false)
	case MBC_3_RAM_BATT_TIMER:
		c.MBC = NewMBC3(rom, c.ROMSize, c.RAMSize, true, true)
	case MBC_5, MBC_5_RAM, MBC_5_RUMBLE, MBC_5_RAM_RUMBLE:
		c.MBC = NewMBC5(rom, c.ROMSize, c.RAMSize, false)
	case MBC_5_RAM_BATT, MBC_5_RAM_BATT_RUMBLE:
		c.MBC = NewMBC5(rom, c.ROMSize, c.RAMSize, true)
	default:
		return errors.New("Error: Cartridge type " + utils.ByteToString(c.Type.ID) + " is currently unsupported")
	}

	return nil
}
func AssertTimings(c *GbcCPU, t *testing.T, instr byte, expectedTiming int, isCB bool) {
	tick := c.Step()

	if isCB {
		log.Println("0xCB "+utils.ByteToString(instr)+" ("+c.CurrentInstruction.Description+")", "testing that instruction runs for", expectedTiming, "cycles")
	} else {
		log.Println(utils.ByteToString(instr)+" ("+c.CurrentInstruction.Description+")", "testing that instruction runs for", expectedTiming, "cycles")
	}

	if tick != expectedTiming {
		if isCB {
			t.Log("-----> For instruction 0xCB", utils.ByteToString(instr)+" ("+c.CurrentInstruction.Description+")", "Expected", expectedTiming, "but got", tick)
		} else {
			t.Log("-----> For instruction", utils.ByteToString(instr)+" ("+c.CurrentInstruction.Description+")", "Expected", expectedTiming, "but got", tick)

		}
		t.FailNow()
	}
}
func getTimingFromInstructionServer(instr byte) int {
	conn, err := net.Dial("tcp", "localhost:8012")
	if err != nil {
		log.Println("error!")
	}
	var writer *bufio.Writer = bufio.NewWriter(conn)
	var reader *bufio.Reader = bufio.NewReader(conn)
	instrString := utils.ByteToString(instr)
	writer.WriteString(instrString + "\n")
	writer.Flush()
	result, _ := reader.ReadString('\n')
	conn.Close()
	i, _ := strconv.Atoi(result)
	return int(i)
}
func (i Instruction) String() string {
	return fmt.Sprintf("%s %s %s  %s", utils.ByteToString(i.Opcode), utils.ByteToString(i.Operands[0]), utils.ByteToString(i.Operands[1]), i.Description)
}