Beispiel #1
0
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("-", 100)) +
		fmt.Sprintln(strings.Join(header, "\n")) +
		fmt.Sprintln(c.MBC) +
		fmt.Sprintln(strings.Repeat("-", 100))
}
Beispiel #2
0
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)
	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()
	}
}
Beispiel #4
0
func (g *DebugOptions) checkWatches(gbc *GomeboyColor) {
	for k, oldVal := range g.watches {
		currentValue := gbc.mmu.ReadByte(k)
		if oldVal != currentValue {
			fmt.Println("Data at memory address", k, "has changed from", utils.ByteToString(oldVal), "to", utils.ByteToString(currentValue))
			fmt.Println("Last operation:", gbc.cpu)
			g.watches[k] = currentValue
		}
	}
}
func getTimingFromInstructionServer(instr byte) int {
	conn, err := net.Dial("tcp", "localhost:8012")
	if err != nil {
		fmt.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)
}
Beispiel #6
0
func (g *DebugOptions) Init(cpuDumpOnStep bool) {
	g.debuggerOn = false
	g.debugFuncMap = make(map[string]DebugCommandHandler)
	g.watches = make(map[types.Word]byte)
	g.stepDump = cpuDumpOnStep
	g.AddDebugFunc("p", "Print CPU state", func(gbc *GomeboyColor, remaining ...string) {
		fmt.Println(gbc.cpu)
	})

	g.AddDebugFunc("r", "Reset", func(gbc *GomeboyColor, remaining ...string) {
		gbc.Reset()
	})

	g.AddDebugFunc("?", "Print this help message", func(gbc *GomeboyColor, remaining ...string) {
		g.help()
	})

	g.AddDebugFunc("help", "Print this help message", func(gbc *GomeboyColor, remaining ...string) {
		g.help()
	})

	g.AddDebugFunc("d", "Disconnect from debugger", func(gbc *GomeboyColor, remaining ...string) {
		gbc.debugOptions.debuggerOn = false
	})

	g.AddDebugFunc("dg", "Dump everything in graphics RAM to a file", func(gbc *GomeboyColor, remaining ...string) {
		var filename string
		if len(remaining) == 0 {
			filename = "gfxdump.png"
			fmt.Println("No filename provided, defaulting to", filename)
		} else {
			filename = remaining[0]
		}

		f, err := os.Create(filename)

		if err != nil {
			fmt.Println("Error creating", filename)
			fmt.Println(err)
			return
		}
		defer f.Close()

		t0unsignedImg, _ := TilemapToImage(gbc.gpu.DumpTilemap(gpu.TILEMAP0, false), "Tilemap 0 unsigned")
		t0signedImg, _ := TilemapToImage(gbc.gpu.DumpTilemap(gpu.TILEMAP0, true), "Tilemap 0 signed")
		t1unsignedImg, _ := TilemapToImage(gbc.gpu.DumpTilemap(gpu.TILEMAP1, false), "Tilemap 1 unsigned")
		t1signedImg, _ := TilemapToImage(gbc.gpu.DumpTilemap(gpu.TILEMAP1, true), "Tilemap 1 signed")
		tilesImg, _ := TilesToImage(gbc.gpu.DumpTiles(), 512, 546)
		spritesImg, _ := SpritesToImage(gbc.gpu.Dump8x8Sprites(), 256, 546) //gbc.gpu.DumpSprites())

		out := image.NewNRGBA(image.Rect(0, 0, 1280, 546))
		draw.Draw(out, image.Rect(0, 0, 512, 546), image.NewUniform(color.White), image.ZP, draw.Src)
		draw.Draw(out, image.Rect(0, 0, 256, 273), t0unsignedImg, image.ZP, draw.Src)
		draw.Draw(out, image.Rect(0, 273, 256, 546), t1unsignedImg, image.Pt(0, 0), draw.Src)
		draw.Draw(out, image.Rect(256, 0, 512, 273), t0signedImg, image.ZP, draw.Src)
		draw.Draw(out, image.Rect(256, 273, 512, 546), t1signedImg, image.Pt(0, 0), draw.Src)
		draw.Draw(out, image.Rect(512, 0, 1024, 546), tilesImg, image.ZP, draw.Src)
		draw.Draw(out, image.Rect(1024, 0, 1280, 546), spritesImg, image.ZP, draw.Src)

		fmt.Println("Dumping to image in file", filename)
		png.Encode(f, out)
		fmt.Println("Done!")
	})

	g.AddDebugFunc("s", "Step", func(gbc *GomeboyColor, remaining ...string) {
		var noOfSteps int = 1
		if len(remaining) > 0 {
			val, err := strconv.ParseInt(remaining[0], 10, 64)
			if err == nil {
				noOfSteps = int(val)
			} else {
				fmt.Println("Cannot parse argument, assuming 1 step forward instead\n\t", err)
				return
			}
		}
		fmt.Println("Stepping forward by", noOfSteps, "instruction(s)")
		for i := 0; i < noOfSteps; i++ {
			gbc.Step()
			if g.stepDump {
				fmt.Println(i, ":", gbc.cpu)
			}
			g.checkWatches(gbc)
		}
		fmt.Println("Current machine state: -")
		fmt.Println(gbc.cpu)
	})

	g.AddDebugFunc("b", "Set breakpoint", func(gbc *GomeboyColor, remaining ...string) {
		if len(remaining) == 0 {
			fmt.Println("You must provide a PC address to break on!")
			return
		}

		var arg string = remaining[0]

		if bp, err := ToMemoryAddress(arg); err != nil {
			fmt.Println("Could not parse memory address argument:", arg)
			fmt.Println("\t", err)
		} else {
			fmt.Println("Setting breakpoint to:", bp)
			g.breakWhen = bp
		}
	})

	g.AddDebugFunc("reg", "Set register", func(gbc *GomeboyColor, remaining ...string) {
		if len(remaining) < 2 {
			fmt.Println("You must provide a register and value!")
			return
		}

		var register string = strings.ToLower(remaining[0])
		value, err := utils.StringToByte(remaining[1])

		if err != nil {
			fmt.Println("Could not parse value", remaining[1])
			fmt.Println("\t", err)
			return
		}

		fmt.Println("Attempting to set register", register, "with value", utils.ByteToString(value))
		switch register {
		case "a":
			gbc.cpu.R.A = value
		case "b":
			gbc.cpu.R.B = value
		case "c":
			gbc.cpu.R.C = value
		case "d":
			gbc.cpu.R.D = value
		case "e":
			gbc.cpu.R.E = value
		case "h":
			gbc.cpu.R.H = value
		case "l":
			gbc.cpu.R.L = value
		default:
			fmt.Println("Unknown register:", register)
		}
	})

	g.AddDebugFunc("rm", "Read data from memory", func(gbc *GomeboyColor, remaining ...string) {
		var startAddr types.Word
		switch len(remaining) {
		case 0:
			fmt.Println("You must provide at least a starting address to inspect")
			return
		default:
			addr, err := ToMemoryAddress(remaining[0])
			if err != nil {
				fmt.Println("Could not parse memory address: ", remaining[0])
				return
			}
			startAddr = addr
		}
		lb := startAddr - (startAddr & 0x000F)
		hb := startAddr + (0x0F - (startAddr & 0x000F))
		fmt.Print("\t\t")
		for w := lb; w <= hb; w++ {
			fmt.Printf("   %X ", byte(w%16))

		}
		fmt.Println()

		fmt.Printf("%s\t\t", lb)
		for w := lb; w <= hb; w++ {
			fmt.Print(utils.ByteToString(gbc.mmu.ReadByte(w)), " ")
		}
		fmt.Println()

	})

	g.AddDebugFunc("wm", "Write data to memory", func(gbc *GomeboyColor, remaining ...string) {
		var value byte
		var toAddr types.Word
		switch len(remaining) {
		case 0:
			fmt.Println("You must provide a byte value and address to write to")
			return
		case 1:
			fmt.Println("You must provide a byte value to put in memory")
			return
		default:
			addr, err := ToMemoryAddress(remaining[0])
			if err != nil {
				fmt.Println("Could not parse memory address: ", remaining[0])
				return
			}
			val, err := utils.StringToByte(remaining[1])
			if err != nil {
				fmt.Println("Could not parse value: ", remaining[1], err)
				return
			}
			toAddr = addr
			value = val
		}

		fmt.Println("Writing", utils.ByteToString(value), "to", toAddr)
		gbc.mmu.WriteByte(toAddr, value)
	})

	g.AddDebugFunc("w", "Set memory location to watch for changes", func(gbc *GomeboyColor, remaining ...string) {
		if len(remaining) == 0 {
			fmt.Println("You must provide a memory address to watch!")
			return
		}

		var arg string = remaining[0]

		if m, err := ToMemoryAddress(arg); err != nil {
			fmt.Println("Could not parse memory address argument:", arg)
			fmt.Println("\t", err)
		} else {
			fmt.Println("Watching memory address:", m)
			value := gbc.mmu.ReadByte(m)
			g.watches[m] = value
		}
	})

	g.AddDebugFunc("q", "Quit emulator", func(gbc *GomeboyColor, remaining ...string) {
		os.Exit(0)
	})
}
Beispiel #7
0
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)
}