#import "GBMemoryByteArray.h" #import "GBCompleteByteSlice.h" @implementation GBMemoryByteArray { Document *_document; } - (instancetype) initWithDocument:(Document *)document { if ((self = [super init])) { _document = document; } return self; } - (unsigned long long)length { switch (_mode) { case GBMemoryEntireSpace: return 0x10000; case GBMemoryROM: return 0x8000; case GBMemoryVRAM: return 0x2000; case GBMemoryExternalRAM: return 0x2000; case GBMemoryRAM: return 0x2000; } } - (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 { // 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; } } 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 { return @[[[GBCompleteByteSlice alloc] initWithByteArray:self]]; } - (HFByteArray *)subarrayWithRange:(HFRange)range { unsigned char arr[range.length]; [self copyBytes:arr range:range]; HFByteArray *ret = [[HFBTreeByteArray alloc] init]; HFFullMemoryByteSlice *slice = [[HFFullMemoryByteSlice alloc] initWithData:[NSData dataWithBytes:arr length:range.length]]; [ret insertByteSlice:slice inRange:HFRangeMake(0, 0)]; return ret; } - (void)insertByteSlice:(HFByteSlice *)slice inRange:(HFRange)range { 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; } 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 } 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); } break; } } 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; } 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