From 44618f75c433096f5f38890442ec5742d895c6a6 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 30 Jul 2022 19:09:14 +0300 Subject: [PATCH] Optimize the memory viewer, GB_INTERNAL no longer required --- Cocoa/GBMemoryByteArray.m | 302 ++++++++++++++++++++++++-------------- Core/gb.c | 8 +- Core/gb.h | 1 + HexFiend/HFController.m | 7 +- 4 files changed, 203 insertions(+), 115 deletions(-) diff --git a/Cocoa/GBMemoryByteArray.m b/Cocoa/GBMemoryByteArray.m index a4ff537..55e1bd1 100644 --- a/Cocoa/GBMemoryByteArray.m +++ b/Cocoa/GBMemoryByteArray.m @@ -1,4 +1,3 @@ -#define GB_INTERNAL // Todo: Some memory accesses are being done using the struct directly #import "GBMemoryByteArray.h" #import "GBCompleteByteSlice.h" @@ -32,71 +31,111 @@ } } +- (uint16_t)base +{ + switch (_mode) { + case GBMemoryEntireSpace: return 0; + case GBMemoryROM: return 0; + case GBMemoryVRAM: return 0x8000; + case GBMemoryExternalRAM: return 0xA000; + case GBMemoryRAM: return 0xC000; + } +} + - (void)copyBytes:(unsigned char *)dst range:(HFRange)range { - [_document performAtomicBlock:^{ - uint8_t *_dst = dst; - uint16_t addr = (uint16_t) range.location; - unsigned long long length = range.length; - if (_mode == GBMemoryEntireSpace) { - while (length) { - *(_dst++) = [_document readMemory:addr++]; - length--; + // Do everything in 0x1000 chunks, never cross a 0x1000 boundary + if ((range.location & 0xF000) != ((range.location + range.length) & 0xF000)) { + size_t partial = 0x1000 - (range.location & 0xFFF); + [self copyBytes:dst + partial range:HFRangeMake(range.location + partial, range.length - partial)]; + range.length = partial; + } + range.location += self.base; + + GB_gameboy_t *gb = _document.gameboy; + + switch (range.location >> 12) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: { + uint16_t bank; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_ROM0, NULL, &bank); + memcpy(dst, data + bank * 0x4000 + range.location, range.length); + break; + } + case 0x4: + case 0x5: + case 0x6: + case 0x7: { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_ROM, &size, &bank); + if (_mode != GBMemoryEntireSpace) { + bank = self.selectedBank & (size / 0x4000 - 1); + } + memcpy(dst, data + bank * 0x4000 + range.location - 0x4000, range.length); + break; + } + case 0x8: + case 0x9: { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, &size, &bank); + if (_mode != GBMemoryEntireSpace) { + bank = self.selectedBank & (size / 0x2000 - 1); + } + memcpy(dst, data + bank * 0x2000 + range.location - 0x8000, range.length); + break; + } + case 0xA: + case 0xB: { + // Some carts are special, use memory read directly in full mem mode + if (_mode == GBMemoryEntireSpace) { + case 0xF: + slow_path: + [_document performAtomicBlock:^{ + for (unsigned i = 0; i < range.length; i++) { + dst[i] = GB_safe_read_memory(gb, range.location + i); + } + }]; + break; + } + else { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_CART_RAM, &size, &bank); + bank = self.selectedBank & (size / 0x2000 - 1); + if (size == 0) { + memset(dst, 0xFF, range.length); + } + else if (range.location + range.length - 0xA000 > size) { + goto slow_path; + } + else { + memcpy(dst, data + bank * 0x2000 + range.location - 0xA000, range.length); + } + break; } } - else { - uint8_t *_dst = dst; - uint16_t bank_backup = 0; - GB_gameboy_t *gb = _document.gameboy; - switch (_mode) { - case GBMemoryROM: - bank_backup = gb->mbc_rom_bank; - gb->mbc_rom_bank = self.selectedBank; - break; - case GBMemoryVRAM: - bank_backup = gb->cgb_vram_bank; - if (GB_is_cgb(gb)) { - gb->cgb_vram_bank = self.selectedBank; - } - addr += 0x8000; - break; - case GBMemoryExternalRAM: - bank_backup = gb->mbc_ram_bank; - gb->mbc_ram_bank = self.selectedBank; - addr += 0xA000; - break; - case GBMemoryRAM: - bank_backup = gb->cgb_ram_bank; - if (GB_is_cgb(gb)) { - gb->cgb_ram_bank = self.selectedBank; - } - addr += 0xC000; - break; - default: - assert(false); - } - while (length) { - *(_dst++) = [_document readMemory:addr++]; - length--; - } - switch (_mode) { - case GBMemoryROM: - gb->mbc_rom_bank = bank_backup; - break; - case GBMemoryVRAM: - gb->cgb_vram_bank = bank_backup; - break; - case GBMemoryExternalRAM: - gb->mbc_ram_bank = bank_backup; - break; - case GBMemoryRAM: - gb->cgb_ram_bank = bank_backup; - break; - default: - assert(false); - } + case 0xC: + case 0xE: { + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, NULL, NULL); + memcpy(dst, data + (range.location & 0xFFF), range.length); + break; } - }]; + + case 0xD: { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, &size, &bank); + if (_mode != GBMemoryEntireSpace) { + bank = self.selectedBank & (size / 0x1000 - 1); + } + memcpy(dst, data + bank * 0x1000 + range.location - 0xD000, range.length); + break; + } + } } - (NSArray *)byteSlices @@ -114,65 +153,104 @@ return ret; } -- (void)insertByteSlice:(HFByteSlice *)slice inRange:(HFRange)lrange +- (void)insertByteSlice:(HFByteSlice *)slice inRange:(HFRange)range { - if (slice.length != lrange.length) return; /* Insertion is not allowed, only overwriting. */ - [_document performAtomicBlock:^{ - uint16_t addr = (uint16_t) lrange.location; - uint16_t bank_backup = 0; - GB_gameboy_t *gb = _document.gameboy; - switch (_mode) { - case GBMemoryROM: - bank_backup = gb->mbc_rom_bank; - gb->mbc_rom_bank = self.selectedBank; + if (slice.length != range.length) return; /* Insertion is not allowed, only overwriting. */ + // Do everything in 0x1000 chunks, never cross a 0x1000 boundary + if ((range.location & 0xF000) != ((range.location + range.length) & 0xF000)) { + size_t partial = 0x1000 - (range.location & 0xFFF); + if (slice.length - partial) { + [self insertByteSlice:[slice subsliceWithRange:HFRangeMake(partial, slice.length - partial)] + inRange:HFRangeMake(range.location + partial, range.length - partial)]; + } + range.length = partial; + } + range.location += self.base; + + GB_gameboy_t *gb = _document.gameboy; + + + switch (range.location >> 12) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: { + return; // ROM not writeable + } + case 0x8: + case 0x9: { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, &size, &bank); + if (_mode != GBMemoryEntireSpace) { + bank = self.selectedBank & (size / 0x2000 - 1); + } + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + memcpy(data + bank * 0x2000 + range.location - 0x8000, sliceData, range.length); + break; + } + case 0xA: + case 0xB: { + // Some carts are special, use memory write directly in full mem mode + if (_mode == GBMemoryEntireSpace) { + case 0xF: + slow_path: + [_document performAtomicBlock:^{ + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + for (unsigned i = 0; i < range.length; i++) { + GB_write_memory(gb, range.location + i, sliceData[i]); + } + }]; break; - case GBMemoryVRAM: - bank_backup = gb->cgb_vram_bank; - if (GB_is_cgb(gb)) { - gb->cgb_vram_bank = self.selectedBank; + } + else { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_CART_RAM, &size, &bank); + bank = self.selectedBank & (size / 0x2000 - 1); + if (size == 0) { + // Nothing to write to } - addr += 0x8000; - break; - case GBMemoryExternalRAM: - bank_backup = gb->mbc_ram_bank; - gb->mbc_ram_bank = self.selectedBank; - addr += 0xA000; - break; - case GBMemoryRAM: - bank_backup = gb->cgb_ram_bank; - if (GB_is_cgb(gb)) { - gb->cgb_ram_bank = self.selectedBank; + else if (range.location + range.length - 0xA000 > size) { + goto slow_path; + } + else { + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + memcpy(data + bank * 0x2000 + range.location - 0xA000, sliceData, range.length); } - addr += 0xC000; - break; - default: break; + } } - uint8_t values[lrange.length]; - [slice copyBytes:values range:HFRangeMake(0, lrange.length)]; - uint8_t *src = values; - unsigned long long length = lrange.length; - while (length) { - [_document writeMemory:addr++ value:*(src++)]; - length--; + case 0xC: + case 0xE: { + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, NULL, NULL); + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + memcpy(data + (range.location & 0xFFF), sliceData, range.length); + break; } - switch (_mode) { - case GBMemoryROM: - gb->mbc_rom_bank = bank_backup; - break; - case GBMemoryVRAM: - gb->cgb_vram_bank = bank_backup; - break; - case GBMemoryExternalRAM: - gb->mbc_ram_bank = bank_backup; - break; - case GBMemoryRAM: - gb->cgb_ram_bank = bank_backup; - break; - default: - break; + + case 0xD: { + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, &size, &bank); + if (_mode != GBMemoryEntireSpace) { + bank = self.selectedBank & (size / 0x1000 - 1); + } + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + + memcpy(data + bank * 0x1000 + range.location - 0xD000, sliceData, range.length); + break; } - }]; + } } @end diff --git a/Core/gb.c b/Core/gb.c index 44c8eca..f8a9321 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1728,7 +1728,11 @@ void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t * switch (access) { case GB_DIRECT_ACCESS_ROM: *size = gb->rom_size; - *bank = gb->mbc_rom_bank; + *bank = gb->mbc_rom_bank & (gb->rom_size / 0x4000 - 1); + return gb->rom; + case GB_DIRECT_ACCESS_ROM0: + *size = gb->rom_size; + *bank = gb->mbc_rom0_bank & (gb->rom_size / 0x4000 - 1); return gb->rom; case GB_DIRECT_ACCESS_RAM: *size = gb->ram_size; @@ -1736,7 +1740,7 @@ void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t * return gb->ram; case GB_DIRECT_ACCESS_CART_RAM: *size = gb->mbc_ram_size; - *bank = gb->mbc_ram_bank; + *bank = gb->mbc_ram_bank & (gb->mbc_ram_size / 0x2000 - 1); return gb->mbc_ram; case GB_DIRECT_ACCESS_VRAM: *size = gb->vram_size; diff --git a/Core/gb.h b/Core/gb.h index ca6b011..64e1acd 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -860,6 +860,7 @@ typedef enum { GB_DIRECT_ACCESS_BGP, GB_DIRECT_ACCESS_OBP, GB_DIRECT_ACCESS_IE, + GB_DIRECT_ACCESS_ROM0, // Identical to ROM, but returns the correct rom0 bank in the bank output argument } GB_direct_access_t; /* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank diff --git a/HexFiend/HFController.m b/HexFiend/HFController.m index 74e033c..9d4b1ae 100644 --- a/HexFiend/HFController.m +++ b/HexFiend/HFController.m @@ -279,8 +279,13 @@ static inline Class preferredByteArrayClass(void) { #if ! NDEBUG HFASSERT(range.location >= 0); HFASSERT(range.length >= 0); - HFASSERT(range.location + range.length <= HFULToFP([self totalLineCount])); #endif + if (range.location + range.length > HFULToFP([self totalLineCount])) { + range.location = [self totalLineCount] - range.length; + if (range.location < 0) { + return; + } + } if (! HFFPRangeEqualsRange(range, displayedLineRange)) { displayedLineRange = range; [self _addPropertyChangeBits:HFControllerDisplayedLineRange];