/* * Log logic for memory-mapping for */ #include "gbemu/memory.h" #include "gbemu/video.h" #include "gbemu/cpu.h" #include "common/common.h" #include #include #include #include #include #include #include #define MAX_RAM_LEN (1 << 20) /* Up to 8Mb Cartridge */ #define UNMAP_BOOTROM_ADDR 0xff50 static struct gb_video *video; static struct gb_interrupt *interrupt; static uint8_t ram[MAX_RAM_LEN]; static unsigned char bootrom[0x100] = {0}; static unsigned char cart_low[0x100] = {0}; static int bootrom_mapped = 1; static void load_file_to_buffer(const char *filename, uint8_t *buffer, size_t size) { //TODO: Check error codes like a real programmer :P FILE* prog_file; long filesize; prog_file = fopen(filename, "r"); if(prog_file < 0) { printf("Failed to load game file: %d\n", errno); return; } fseek(prog_file, 0, SEEK_END); filesize = ftell(prog_file); fseek(prog_file, 0, SEEK_SET); if (fread(buffer, MIN(filesize, size), 1, prog_file) < 0) { printf("Failed to read filed: %d\n", errno); } fclose(prog_file); } void gb_mem_init(struct gb_memory *memory, struct gb_video *v, struct gb_interrupt *i, const char *bootrom_file, const char *rom_file) { interrupt = i; video = v; memset(&ram, 0, MAX_RAM_LEN); bootrom_mapped = 1; load_file_to_buffer(rom_file, cart_low, sizeof(cart_low)); load_file_to_buffer(rom_file, ram, sizeof(ram)); load_file_to_buffer(bootrom_file, bootrom, sizeof(bootrom)); load_file_to_buffer(bootrom_file, ram, sizeof(ram)); } static uint8_t ser_byte = 0; static uint8_t ser_ctl = 0; static void gb_serial_write(struct gb_memory *memory, uint16_t addr, uint8_t val) { int fd; switch (addr) { case 0xFF01: ser_byte = val; break; case 0xFF02: if (val == 0x81) { fd = open("serial.log", O_WRONLY | O_CREAT | O_APPEND, S_IRWXU | S_IRGRP | S_IROTH | S_IWUSR); printf("%c", ser_byte); write(fd, &ser_byte, sizeof(ser_byte)); close(fd); } break; default: ASSERT(0); } } static uint8_t gb_serial_read(struct gb_memory *memory, uint16_t addr) { int fd; static uint8_t ser_byte = 0; switch (addr) { case 0xFF01: return ser_byte; case 0xFF02: return ser_ctl; default: ASSERT(0); } } uint8_t gb_mem_read(struct gb_memory *memory, uint16_t addr) { switch (addr) { case 0xFF01 ... 0xFF02: return gb_serial_read(memory, addr); case 0xFF40 ... 0xFF4B: return gb_video_mem_read(video, addr); case 0xFF0F: return gb_interrupt_if_read(interrupt); case 0xFFFF: return gb_interrupt_ie_read(interrupt); default: return ram[addr]; } } void gb_mbc3_write(struct gb_memory *memory, uint16_t addr, uint8_t val) { switch (addr) { case 0x0000 ... 0x1FFF: /* RAM and Timer Enable */ ASSERT(0); case 0xA000 ... 0xBFFF: /* RAM Bank */ ASSERT(0); case 0x2000 ... 0x3FFF: /* ROM Bank Number */ ASSERT(0); case 0x4000 ... 0x5FFF: /* RAM Bank Number / RTC Reg Select */ ASSERT(0); case 0x6000 ... 0x7FFF: /* Latch Clock Data */ ASSERT(0); case 0xFF01 ... 0xFF02: gb_serial_write(memory, addr, val); break; case 0xFF40 ... 0xFF4B: gb_video_mem_write(video, addr, val); break; case 0xFF0F: gb_interrupt_if_write(interrupt, val); break; case 0xFFFF: gb_interrupt_ie_write(interrupt, val); break; default: ram[addr] = val; } } void gb_mem_write(struct gb_memory *memory, uint16_t addr, uint8_t val) { if (!bootrom_mapped && ram[0x147] == 0x05) { gb_mbc3_write(memory, addr, val); return; } switch (addr) { case UNMAP_BOOTROM_ADDR: if (val == 1) { bootrom_mapped = 0; memcpy(ram, cart_low, sizeof(cart_low)); } return; case 0xFF01 ... 0xFF02: gb_serial_write(memory, addr, val); return; case 0xFF40 ... 0xFF4B: gb_video_mem_write(video, addr, val); return; case 0xFF0F: gb_interrupt_if_write(interrupt, val); return; case 0xFFFF: gb_interrupt_ie_write(interrupt, val); return; case 0x8000 ... 0x87FF: case 0x9800 ... 0x9BFF: break; case 0 ... 0x100: if (bootrom_mapped) { return; } break; } ram[addr] = val; } void gb_mem_force_write(struct gb_memory *memory, uint16_t addr, uint8_t val) { switch (addr) { case UNMAP_BOOTROM_ADDR: break; case 0xFF40 ... 0xFF4B: gb_video_mem_write(video, addr, val); break; case 0 ... 0x100: /* Intentional fall-through */ default: ram[addr] = val; } }