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)) }
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() } }
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) }
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) }) }
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) }