From a16cbdc4163e895323c1b92ff5ff7f4e925a99b5 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 10 Dec 2014 05:05:41 +0100 Subject: Rename memory.c to gba_memory.c --- Makefile.common | 2 +- gba_memory.c | 3556 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.c | 3556 ------------------------------------------------------- 3 files changed, 3557 insertions(+), 3557 deletions(-) create mode 100644 gba_memory.c delete mode 100644 memory.c diff --git a/Makefile.common b/Makefile.common index 39dd9e9..aef6d23 100644 --- a/Makefile.common +++ b/Makefile.common @@ -7,7 +7,7 @@ SOURCES_C := $(CORE_DIR)/gpsp_griffin.c else SOURCES_C := $(CORE_DIR)/main.c \ $(CORE_DIR)/cpu.c \ - $(CORE_DIR)/memory.c \ + $(CORE_DIR)/gba_memory.c \ $(CORE_DIR)/video.c \ $(CORE_DIR)/input.c \ $(CORE_DIR)/sound.c \ diff --git a/gba_memory.c b/gba_memory.c new file mode 100644 index 0000000..b93e928 --- /dev/null +++ b/gba_memory.c @@ -0,0 +1,3556 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" + +/* Sound */ +#define gbc_sound_tone_control_low(channel, address) \ +{ \ + u32 initial_volume = (value >> 12) & 0x0F; \ + u32 envelope_ticks = ((value >> 8) & 0x07) * 4; \ + gbc_sound_channel[channel].length_ticks = 64 - (value & 0x3F); \ + gbc_sound_channel[channel].sample_data = \ + square_pattern_duty[(value >> 6) & 0x03]; \ + gbc_sound_channel[channel].envelope_direction = (value >> 11) & 0x01; \ + gbc_sound_channel[channel].envelope_initial_volume = initial_volume; \ + gbc_sound_channel[channel].envelope_volume = initial_volume; \ + gbc_sound_channel[channel].envelope_initial_ticks = envelope_ticks; \ + gbc_sound_channel[channel].envelope_ticks = envelope_ticks; \ + gbc_sound_channel[channel].envelope_status = (envelope_ticks != 0); \ + gbc_sound_channel[channel].envelope_volume = initial_volume; \ + gbc_sound_update = 1; \ + address16(io_registers, address) = value; \ +} \ + +#define gbc_sound_tone_control_high(channel, address) \ +{ \ + u32 rate = value & 0x7FF; \ + gbc_sound_channel[channel].rate = rate; \ + gbc_sound_channel[channel].frequency_step = \ + float_to_fp16_16(((131072.0 / (2048 - rate)) * 8.0) / sound_frequency); \ + gbc_sound_channel[channel].length_status = (value >> 14) & 0x01; \ + if(value & 0x8000) \ + { \ + gbc_sound_channel[channel].active_flag = 1; \ + gbc_sound_channel[channel].sample_index -= float_to_fp16_16(1.0 / 12.0); \ + gbc_sound_channel[channel].envelope_ticks = \ + gbc_sound_channel[channel].envelope_initial_ticks; \ + gbc_sound_channel[channel].envelope_volume = \ + gbc_sound_channel[channel].envelope_initial_volume; \ + } \ + \ + gbc_sound_update = 1; \ + address16(io_registers, address) = value; \ +} \ + +#define gbc_sound_tone_control_sweep() \ +{ \ + u32 sweep_ticks = ((value >> 4) & 0x07) * 2; \ + gbc_sound_channel[0].sweep_shift = value & 0x07; \ + gbc_sound_channel[0].sweep_direction = (value >> 3) & 0x01; \ + gbc_sound_channel[0].sweep_status = (value != 8); \ + gbc_sound_channel[0].sweep_ticks = sweep_ticks; \ + gbc_sound_channel[0].sweep_initial_ticks = sweep_ticks; \ + gbc_sound_update = 1; \ + address16(io_registers, 0x60) = value; \ +} \ + +#define gbc_sound_wave_control() \ +{ \ + gbc_sound_channel[2].wave_type = (value >> 5) & 0x01; \ + gbc_sound_channel[2].wave_bank = (value >> 6) & 0x01; \ + if(value & 0x80) \ + { \ + gbc_sound_channel[2].master_enable = 1; \ + } \ + else \ + { \ + gbc_sound_channel[2].master_enable = 0; \ + } \ + \ + gbc_sound_update = 1; \ + address16(io_registers, 0x70) = value; \ +} \ + +static u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 }; + +#define gbc_sound_tone_control_low_wave() \ +{ \ + gbc_sound_channel[2].length_ticks = 256 - (value & 0xFF); \ + if((value >> 15) & 0x01) \ + { \ + gbc_sound_channel[2].wave_volume = 12288; \ + } \ + else \ + { \ + gbc_sound_channel[2].wave_volume = \ + gbc_sound_wave_volume[(value >> 13) & 0x03]; \ + } \ + gbc_sound_update = 1; \ + address16(io_registers, 0x72) = value; \ +} \ + +#define gbc_sound_tone_control_high_wave() \ +{ \ + u32 rate = value & 0x7FF; \ + gbc_sound_channel[2].rate = rate; \ + gbc_sound_channel[2].frequency_step = \ + float_to_fp16_16((2097152.0 / (2048 - rate)) / sound_frequency); \ + gbc_sound_channel[2].length_status = (value >> 14) & 0x01; \ + if(value & 0x8000) \ + { \ + gbc_sound_channel[2].sample_index = 0; \ + gbc_sound_channel[2].active_flag = 1; \ + } \ + gbc_sound_update = 1; \ + address16(io_registers, 0x74) = value; \ +} \ + +#define gbc_sound_noise_control() \ +{ \ + u32 dividing_ratio = value & 0x07; \ + u32 frequency_shift = (value >> 4) & 0x0F; \ + if(dividing_ratio == 0) \ + { \ + gbc_sound_channel[3].frequency_step = \ + float_to_fp16_16(1048576.0 / (1 << (frequency_shift + 1)) / \ + sound_frequency); \ + } \ + else \ + { \ + gbc_sound_channel[3].frequency_step = \ + float_to_fp16_16(524288.0 / (dividing_ratio * \ + (1 << (frequency_shift + 1))) / sound_frequency); \ + } \ + gbc_sound_channel[3].noise_type = (value >> 3) & 0x01; \ + gbc_sound_channel[3].length_status = (value >> 14) & 0x01; \ + if(value & 0x8000) \ + { \ + gbc_sound_channel[3].sample_index = 0; \ + gbc_sound_channel[3].active_flag = 1; \ + gbc_sound_channel[3].envelope_ticks = \ + gbc_sound_channel[3].envelope_initial_ticks; \ + gbc_sound_channel[3].envelope_volume = \ + gbc_sound_channel[3].envelope_initial_volume; \ + } \ + gbc_sound_update = 1; \ + address16(io_registers, 0x7C) = value; \ +} \ + +#define gbc_trigger_sound_channel(channel) \ + gbc_sound_master_volume_right = value & 0x07; \ + gbc_sound_master_volume_left = (value >> 4) & 0x07; \ + gbc_sound_channel[channel].status = ((value >> (channel + 8)) & 0x01) | \ + ((value >> (channel + 11)) & 0x03) \ + +#define gbc_trigger_sound() \ +{ \ + gbc_trigger_sound_channel(0); \ + gbc_trigger_sound_channel(1); \ + gbc_trigger_sound_channel(2); \ + gbc_trigger_sound_channel(3); \ + address16(io_registers, 0x80) = value; \ +} \ + +#define trigger_sound() \ +{ \ + timer[0].direct_sound_channels = (((value >> 10) & 0x01) == 0) | \ + ((((value >> 14) & 0x01) == 0) << 1); \ + timer[1].direct_sound_channels = (((value >> 10) & 0x01) == 1) | \ + ((((value >> 14) & 0x01) == 1) << 1); \ + direct_sound_channel[0].volume = (value >> 2) & 0x01; \ + direct_sound_channel[0].status = (value >> 8) & 0x03; \ + direct_sound_channel[1].volume = (value >> 3) & 0x01; \ + direct_sound_channel[1].status = (value >> 12) & 0x03; \ + gbc_sound_master_volume = value & 0x03; \ + \ + if((value >> 11) & 0x01) \ + sound_reset_fifo(0); \ + if((value >> 15) & 0x01) \ + sound_reset_fifo(1); \ + address16(io_registers, 0x82) = value; \ +} \ + +#define sound_on() \ + if(value & 0x80) \ + { \ + if(sound_on != 1) \ + { \ + sound_on = 1; \ + } \ + } \ + else \ + { \ + u32 i; \ + for(i = 0; i < 4; i++) \ + { \ + gbc_sound_channel[i].active_flag = 0; \ + } \ + sound_on = 0; \ + } \ + address16(io_registers, 0x84) = \ + (address16(io_registers, 0x84) & 0x000F) | (value & 0xFFF0); \ + +#define sound_update_frequency_step(timer_number) \ + timer[timer_number].frequency_step = \ + float_to_fp8_24(GBC_BASE_RATE / (timer_reload * sound_frequency)) \ + +/* Main */ +extern timer_type timer[4]; +static u32 prescale_table[] = { 0, 6, 8, 10 }; + +#define count_timer(timer_number) \ + timer[timer_number].reload = 0x10000 - value; \ + if(timer_number < 2) \ + { \ + u32 timer_reload = \ + timer[timer_number].reload << timer[timer_number].prescale; \ + sound_update_frequency_step(timer_number); \ + } \ + +#define adjust_sound_buffer(timer_number, channel) \ + if(timer[timer_number].direct_sound_channels & (0x01 << channel)) \ + { \ + direct_sound_channel[channel].buffer_index = \ + (gbc_sound_buffer_index + buffer_adjust) % BUFFER_SIZE; \ + } \ + +#define trigger_timer(timer_number) \ + if(value & 0x80) \ + { \ + if(timer[timer_number].status == TIMER_INACTIVE) \ + { \ + u32 prescale = prescale_table[value & 0x03]; \ + u32 timer_reload = timer[timer_number].reload; \ + \ + if((value >> 2) & 0x01) \ + timer[timer_number].status = TIMER_CASCADE; \ + else \ + timer[timer_number].status = TIMER_PRESCALE; \ + \ + timer[timer_number].prescale = prescale; \ + timer[timer_number].irq = (value >> 6) & 0x01; \ + \ + address16(io_registers, 0x100 + (timer_number * 4)) = \ + -timer_reload; \ + \ + timer_reload <<= prescale; \ + timer[timer_number].count = timer_reload; \ + \ + if(timer_reload < execute_cycles) \ + execute_cycles = timer_reload; \ + \ + if(timer_number < 2) \ + { \ + u32 buffer_adjust = \ + (u32)(((float)(cpu_ticks - gbc_sound_last_cpu_ticks) * \ + sound_frequency) / GBC_BASE_RATE) * 2; \ + \ + sound_update_frequency_step(timer_number); \ + adjust_sound_buffer(timer_number, 0); \ + adjust_sound_buffer(timer_number, 1); \ + } \ + } \ + } \ + else \ + { \ + if(timer[timer_number].status != TIMER_INACTIVE) \ + { \ + timer[timer_number].status = TIMER_INACTIVE; \ + timer[timer_number].stop_cpu_ticks = cpu_ticks; \ + } \ + } \ + address16(io_registers, 0x102 + (timer_number * 4)) = value; \ + +// This table is configured for sequential access on system defaults + +u32 waitstate_cycles_sequential[16][3] = +{ + { 1, 1, 1 }, // BIOS + { 1, 1, 1 }, // Invalid + { 3, 3, 6 }, // EWRAM (default settings) + { 1, 1, 1 }, // IWRAM + { 1, 1, 1 }, // IO Registers + { 1, 1, 2 }, // Palette RAM + { 1, 1, 2 }, // VRAM + { 1, 1, 2 }, // OAM + { 3, 3, 6 }, // Gamepak (wait 0) + { 3, 3, 6 }, // Gamepak (wait 0) + { 5, 5, 9 }, // Gamepak (wait 1) + { 5, 5, 9 }, // Gamepak (wait 1) + { 9, 9, 17 }, // Gamepak (wait 2) + { 9, 9, 17 }, // Gamepak (wait 2) +}; + +// Different settings for gamepak ws0-2 sequential (2nd) access + +u32 gamepak_waitstate_sequential[2][3][3] = +{ + { + { 3, 3, 6 }, + { 5, 5, 9 }, + { 9, 9, 17 } + }, + { + { 2, 2, 3 }, + { 2, 2, 3 }, + { 2, 2, 3 } + } +}; + +u16 palette_ram[512]; +u16 oam_ram[512]; +u16 palette_ram_converted[512]; +u16 io_registers[1024 * 16]; +u8 ewram[1024 * 256 * 2]; +u8 iwram[1024 * 32 * 2]; +u8 vram[1024 * 96 * 2]; + +u8 bios_rom[1024 * 32]; +u32 bios_read_protect; + +// Up to 128kb, store SRAM, flash ROM, or EEPROM here. +u8 gamepak_backup[1024 * 128]; + +// Keeps us knowing how much we have left. +u8 *gamepak_rom; +u32 gamepak_size; + +dma_transfer_type dma[4]; + +u8 *memory_regions[16]; +u32 memory_limits[16]; + +typedef struct +{ + u32 page_timestamp; + u32 physical_index; +} gamepak_swap_entry_type; + +u32 gamepak_ram_buffer_size; +u32 gamepak_ram_pages; + +// Enough to map the gamepak RAM space. +gamepak_swap_entry_type *gamepak_memory_map; + +// This is global so that it can be kept open for large ROMs to swap +// pages from, so there's no slowdown with opening and closing the file +// a lot. +#ifdef PSP_BUILD + +file_tag_type gamepak_file_large = -1; + +#else + +file_tag_type gamepak_file_large = NULL; + +#endif + +u32 direct_map_vram = 0; + +// Writes to these respective locations should trigger an update +// so the related subsystem may react to it. + +// If OAM is written to: +u32 oam_update = 1; + +// If GBC audio is written to: +u32 gbc_sound_update = 0; + +// If the GBC audio waveform is modified: +u32 gbc_sound_wave_update = 0; + +// If the backup space is written (only update once this hits 0) +u32 backup_update = 0; + +// Write out backup file this many cycles after the most recent +// backup write. +const u32 write_backup_delay = 10; + + +typedef enum +{ + BACKUP_SRAM, + BACKUP_FLASH, + BACKUP_EEPROM, + BACKUP_NONE +} backup_type_type; + +typedef enum +{ + SRAM_SIZE_32KB, + SRAM_SIZE_64KB +} sram_size_type; + +// Keep it 32KB until the upper 64KB is accessed, then make it 64KB. + +backup_type_type backup_type = BACKUP_NONE; +sram_size_type sram_size = SRAM_SIZE_32KB; + +typedef enum +{ + FLASH_BASE_MODE, + FLASH_ERASE_MODE, + FLASH_ID_MODE, + FLASH_WRITE_MODE, + FLASH_BANKSWITCH_MODE +} flash_mode_type; + +typedef enum +{ + FLASH_SIZE_64KB, + FLASH_SIZE_128KB +} flash_size_type; + +flash_mode_type flash_mode = FLASH_BASE_MODE; +u32 flash_command_position = 0; +u8 *flash_bank_ptr = gamepak_backup; + +flash_device_id_type flash_device_id = FLASH_DEVICE_MACRONIX_64KB; +flash_manufacturer_id_type flash_manufacturer_id = + FLASH_MANUFACTURER_MACRONIX; +flash_size_type flash_size = FLASH_SIZE_64KB; + +u8 read_backup(u32 address) +{ + u8 value = 0; + + if(backup_type == BACKUP_NONE) + backup_type = BACKUP_SRAM; + + if(backup_type == BACKUP_SRAM) + { + value = gamepak_backup[address]; + } + else + + if(flash_mode == FLASH_ID_MODE) + { + /* ID manufacturer type */ + if(address == 0x0000) + value = flash_manufacturer_id; + else + + /* ID device type */ + if(address == 0x0001) + value = flash_device_id; + } + else + { + value = flash_bank_ptr[address]; + } + + return value; +} + +#define read_backup8() \ + value = read_backup(address & 0xFFFF) \ + +#define read_backup16() \ + value = 0 \ + +#define read_backup32() \ + value = 0 \ + + +// EEPROM is 512 bytes by default; it is autodetecte as 8KB if +// 14bit address DMAs are made (this is done in the DMA handler). + +typedef enum +{ + EEPROM_512_BYTE, + EEPROM_8_KBYTE +} eeprom_size_type; + +typedef enum +{ + EEPROM_BASE_MODE, + EEPROM_READ_MODE, + EEPROM_READ_HEADER_MODE, + EEPROM_ADDRESS_MODE, + EEPROM_WRITE_MODE, + EEPROM_WRITE_ADDRESS_MODE, + EEPROM_ADDRESS_FOOTER_MODE, + EEPROM_WRITE_FOOTER_MODE +} eeprom_mode_type; + + +eeprom_size_type eeprom_size = EEPROM_512_BYTE; +eeprom_mode_type eeprom_mode = EEPROM_BASE_MODE; +u32 eeprom_address_length; +u32 eeprom_address = 0; +s32 eeprom_counter = 0; +u8 eeprom_buffer[8]; + + +void function_cc write_eeprom(u32 address, u32 value) +{ + switch(eeprom_mode) + { + case EEPROM_BASE_MODE: + backup_type = BACKUP_EEPROM; + eeprom_buffer[0] |= (value & 0x01) << (1 - eeprom_counter); + eeprom_counter++; + if(eeprom_counter == 2) + { + if(eeprom_size == EEPROM_512_BYTE) + eeprom_address_length = 6; + else + eeprom_address_length = 14; + + eeprom_counter = 0; + + switch(eeprom_buffer[0] & 0x03) + { + case 0x02: + eeprom_mode = EEPROM_WRITE_ADDRESS_MODE; + break; + + case 0x03: + eeprom_mode = EEPROM_ADDRESS_MODE; + break; + } + address16(eeprom_buffer, 0) = 0; + } + break; + + case EEPROM_ADDRESS_MODE: + case EEPROM_WRITE_ADDRESS_MODE: + eeprom_buffer[eeprom_counter / 8] + |= (value & 0x01) << (7 - (eeprom_counter % 8)); + eeprom_counter++; + if(eeprom_counter == eeprom_address_length) + { + if(eeprom_size == EEPROM_512_BYTE) + { + eeprom_address = + (address16(eeprom_buffer, 0) >> 2) * 8; + } + else + { + eeprom_address = (((u32)eeprom_buffer[1] >> 2) | + ((u32)eeprom_buffer[0] << 6)) * 8; + } + + address16(eeprom_buffer, 0) = 0; + eeprom_counter = 0; + + if(eeprom_mode == EEPROM_ADDRESS_MODE) + { + eeprom_mode = EEPROM_ADDRESS_FOOTER_MODE; + } + else + { + eeprom_mode = EEPROM_WRITE_MODE; + memset(gamepak_backup + eeprom_address, 0, 8); + } + } + break; + + case EEPROM_WRITE_MODE: + gamepak_backup[eeprom_address + (eeprom_counter / 8)] |= + (value & 0x01) << (7 - (eeprom_counter % 8)); + eeprom_counter++; + if(eeprom_counter == 64) + { + backup_update = write_backup_delay; + eeprom_counter = 0; + eeprom_mode = EEPROM_WRITE_FOOTER_MODE; + } + break; + + case EEPROM_ADDRESS_FOOTER_MODE: + case EEPROM_WRITE_FOOTER_MODE: + eeprom_counter = 0; + if(eeprom_mode == EEPROM_ADDRESS_FOOTER_MODE) + { + eeprom_mode = EEPROM_READ_HEADER_MODE; + } + else + { + eeprom_mode = EEPROM_BASE_MODE; + } + break; + + default: + break; + } +} + +#define read_memory_gamepak(type) \ + u32 gamepak_index = address >> 15; \ + u8 *map = memory_map_read[gamepak_index]; \ + \ + if(map == NULL) \ + map = load_gamepak_page(gamepak_index & 0x3FF); \ + \ + value = address##type(map, address & 0x7FFF) \ + +#define read_open8() \ + if(!(reg[REG_CPSR] & 0x20)) \ + value = read_memory8(reg[REG_PC] + 4 + (address & 0x03)); \ + else \ + value = read_memory8(reg[REG_PC] + 2 + (address & 0x01)) \ + +#define read_open16() \ + if(!(reg[REG_CPSR] & 0x20)) \ + value = read_memory16(reg[REG_PC] + 4 + (address & 0x02)); \ + else \ + value = read_memory16(reg[REG_PC] + 2) \ + +#define read_open32() \ + if(!(reg[REG_CPSR] & 0x20)) \ + { \ + value = read_memory32(reg[REG_PC] + 4); \ + } \ + else \ + { \ + u32 current_instruction = read_memory16(reg[REG_PC] + 2); \ + value = current_instruction | (current_instruction << 16); \ + } \ + +u32 function_cc read_eeprom() +{ + u32 value; + + switch(eeprom_mode) + { + case EEPROM_BASE_MODE: + value = 1; + break; + + case EEPROM_READ_MODE: + value = (gamepak_backup[eeprom_address + (eeprom_counter / 8)] >> + (7 - (eeprom_counter % 8))) & 0x01; + eeprom_counter++; + if(eeprom_counter == 64) + { + eeprom_counter = 0; + eeprom_mode = EEPROM_BASE_MODE; + } + break; + + case EEPROM_READ_HEADER_MODE: + value = 0; + eeprom_counter++; + if(eeprom_counter == 4) + { + eeprom_mode = EEPROM_READ_MODE; + eeprom_counter = 0; + } + break; + + default: + value = 0; + break; + } + + return value; +} + + +#define read_memory(type) \ + switch(address >> 24) \ + { \ + case 0x00: \ + /* BIOS */ \ + if(reg[REG_PC] >= 0x4000) \ + value = address##type(&bios_read_protect, address & 0x03); \ + else \ + value = address##type(bios_rom, address & 0x3FFF); \ + break; \ + \ + case 0x02: \ + /* external work RAM */ \ + address = (address & 0x7FFF) + ((address & 0x38000) * 2) + 0x8000; \ + value = address##type(ewram, address); \ + break; \ + \ + case 0x03: \ + /* internal work RAM */ \ + value = address##type(iwram, (address & 0x7FFF) + 0x8000); \ + break; \ + \ + case 0x04: \ + /* I/O registers */ \ + value = address##type(io_registers, address & 0x3FF); \ + break; \ + \ + case 0x05: \ + /* palette RAM */ \ + value = address##type(palette_ram, address & 0x3FF); \ + break; \ + \ + case 0x06: \ + /* VRAM */ \ + address &= 0x1FFFF; \ + if(address > 0x18000) \ + address -= 0x8000; \ + \ + value = address##type(vram, address); \ + break; \ + \ + case 0x07: \ + /* OAM RAM */ \ + value = address##type(oam_ram, address & 0x3FF); \ + break; \ + \ + case 0x08: \ + case 0x09: \ + case 0x0A: \ + case 0x0B: \ + case 0x0C: \ + /* gamepak ROM */ \ + if((address & 0x1FFFFFF) >= gamepak_size) \ + { \ + value = 0; \ + } \ + else \ + { \ + read_memory_gamepak(type); \ + } \ + break; \ + \ + case 0x0D: \ + if((address & 0x1FFFFFF) < gamepak_size) \ + { \ + read_memory_gamepak(type); \ + } \ + else \ + { \ + value = read_eeprom(); \ + } \ + \ + break; \ + \ + case 0x0E: \ + case 0x0F: \ + read_backup##type(); \ + break; \ + \ + default: \ + read_open##type(); \ + break; \ + } \ + +#define trigger_dma(dma_number) \ + if(value & 0x8000) \ + { \ + if(dma[dma_number].start_type == DMA_INACTIVE) \ + { \ + u32 start_type = (value >> 12) & 0x03; \ + u32 dest_address = address32(io_registers, (dma_number * 12) + 0xB4) & \ + 0xFFFFFFF; \ + \ + dma[dma_number].dma_channel = dma_number; \ + dma[dma_number].source_address = \ + address32(io_registers, (dma_number * 12) + 0xB0) & 0xFFFFFFF; \ + dma[dma_number].dest_address = dest_address; \ + dma[dma_number].source_direction = (value >> 7) & 0x03; \ + dma[dma_number].repeat_type = (value >> 9) & 0x01; \ + dma[dma_number].start_type = start_type; \ + dma[dma_number].irq = (value >> 14) & 0x01; \ + \ + /* If it is sound FIFO DMA make sure the settings are a certain way */ \ + if((dma_number >= 1) && (dma_number <= 2) && \ + (start_type == DMA_START_SPECIAL)) \ + { \ + dma[dma_number].length_type = DMA_32BIT; \ + dma[dma_number].length = 4; \ + dma[dma_number].dest_direction = DMA_FIXED; \ + if(dest_address == 0x40000A4) \ + dma[dma_number].direct_sound_channel = DMA_DIRECT_SOUND_B; \ + else \ + dma[dma_number].direct_sound_channel = DMA_DIRECT_SOUND_A; \ + } \ + else \ + { \ + u32 length = \ + address16(io_registers, (dma_number * 12) + 0xB8); \ + \ + if((dma_number == 3) && ((dest_address >> 24) == 0x0D) && \ + ((length & 0x1F) == 17)) \ + { \ + eeprom_size = EEPROM_8_KBYTE; \ + } \ + \ + if(dma_number < 3) \ + length &= 0x3FFF; \ + \ + if(length == 0) \ + { \ + if(dma_number == 3) \ + length = 0x10000; \ + else \ + length = 0x04000; \ + } \ + \ + dma[dma_number].length = length; \ + dma[dma_number].length_type = (value >> 10) & 0x01; \ + dma[dma_number].dest_direction = (value >> 5) & 0x03; \ + } \ + \ + address16(io_registers, (dma_number * 12) + 0xBA) = value; \ + if(start_type == DMA_START_IMMEDIATELY) \ + return dma_transfer(dma + dma_number); \ + } \ + } \ + else \ + { \ + dma[dma_number].start_type = DMA_INACTIVE; \ + dma[dma_number].direct_sound_channel = DMA_NO_DIRECT_SOUND; \ + address16(io_registers, (dma_number * 12) + 0xBA) = value; \ + } \ + + +#define access_register8_high(address) \ + value = (value << 8) | (address8(io_registers, address)) \ + +#define access_register8_low(address) \ + value = ((address8(io_registers, address + 1)) << 8) | value \ + +#define access_register16_high(address) \ + value = (value << 16) | (address16(io_registers, address)) \ + +#define access_register16_low(address) \ + value = ((address16(io_registers, address + 2)) << 16) | value \ + +cpu_alert_type function_cc write_io_register8(u32 address, u32 value) +{ + switch(address) + { + case 0x00: + { + u32 dispcnt = io_registers[REG_DISPCNT]; + + if((value & 0x07) != (dispcnt & 0x07)) + oam_update = 1; + + address8(io_registers, 0x00) = value; + break; + } + + // DISPSTAT (lower byte) + case 0x04: + address8(io_registers, 0x04) = + (address8(io_registers, 0x04) & 0x07) | (value & ~0x07); + break; + + // VCOUNT + case 0x06: + case 0x07: + break; + + // BG2 reference X + case 0x28: + access_register8_low(0x28); + access_register16_low(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + case 0x29: + access_register8_high(0x28); + access_register16_low(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + case 0x2A: + access_register8_low(0x2A); + access_register16_high(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + case 0x2B: + access_register8_high(0x2A); + access_register16_high(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + // BG2 reference Y + case 0x2C: + access_register8_low(0x2C); + access_register16_low(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + case 0x2D: + access_register8_high(0x2C); + access_register16_low(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + case 0x2E: + access_register8_low(0x2E); + access_register16_high(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + case 0x2F: + access_register8_high(0x2E); + access_register16_high(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + // BG3 reference X + case 0x38: + access_register8_low(0x38); + access_register16_low(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + case 0x39: + access_register8_high(0x38); + access_register16_low(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + case 0x3A: + access_register8_low(0x3A); + access_register16_high(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + case 0x3B: + access_register8_high(0x3A); + access_register16_high(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + // BG3 reference Y + case 0x3C: + access_register8_low(0x3C); + access_register16_low(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + case 0x3D: + access_register8_high(0x3C); + access_register16_low(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + case 0x3E: + access_register8_low(0x3E); + access_register16_high(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + case 0x3F: + access_register8_high(0x3E); + access_register16_high(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + // Sound 1 control sweep + case 0x60: + access_register8_low(0x60); + gbc_sound_tone_control_sweep(); + break; + + case 0x61: + access_register8_low(0x60); + gbc_sound_tone_control_sweep(); + break; + + // Sound 1 control duty/length/envelope + case 0x62: + access_register8_low(0x62); + gbc_sound_tone_control_low(0, 0x62); + break; + + case 0x63: + access_register8_high(0x62); + gbc_sound_tone_control_low(0, 0x62); + break; + + // Sound 1 control frequency + case 0x64: + access_register8_low(0x64); + gbc_sound_tone_control_high(0, 0x64); + break; + + case 0x65: + access_register8_high(0x64); + gbc_sound_tone_control_high(0, 0x64); + break; + + // Sound 2 control duty/length/envelope + case 0x68: + access_register8_low(0x68); + gbc_sound_tone_control_low(1, 0x68); + break; + + case 0x69: + access_register8_high(0x68); + gbc_sound_tone_control_low(1, 0x68); + break; + + // Sound 2 control frequency + case 0x6C: + access_register8_low(0x6C); + gbc_sound_tone_control_high(1, 0x6C); + break; + + case 0x6D: + access_register8_high(0x6C); + gbc_sound_tone_control_high(1, 0x6C); + break; + + // Sound 3 control wave + case 0x70: + access_register8_low(0x70); + gbc_sound_wave_control(); + break; + + case 0x71: + access_register8_high(0x70); + gbc_sound_wave_control(); + break; + + // Sound 3 control length/volume + case 0x72: + access_register8_low(0x72); + gbc_sound_tone_control_low_wave(); + break; + + case 0x73: + access_register8_high(0x72); + gbc_sound_tone_control_low_wave(); + break; + + // Sound 3 control frequency + case 0x74: + access_register8_low(0x74); + gbc_sound_tone_control_high_wave(); + break; + + case 0x75: + access_register8_high(0x74); + gbc_sound_tone_control_high_wave(); + break; + + // Sound 4 control length/envelope + case 0x78: + access_register8_low(0x78); + gbc_sound_tone_control_low(3, 0x78); + break; + + case 0x79: + access_register8_high(0x78); + gbc_sound_tone_control_low(3, 0x78); + break; + + // Sound 4 control frequency + case 0x7C: + access_register8_low(0x7C); + gbc_sound_noise_control(); + break; + + case 0x7D: + access_register8_high(0x7C); + gbc_sound_noise_control(); + break; + + // Sound control L + case 0x80: + access_register8_low(0x80); + gbc_trigger_sound(); + break; + + case 0x81: + access_register8_high(0x80); + gbc_trigger_sound(); + break; + + // Sound control H + case 0x82: + access_register8_low(0x82); + trigger_sound(); + break; + + case 0x83: + access_register8_high(0x82); + trigger_sound(); + break; + + // Sound control X + case 0x84: + sound_on(); + break; + + // Sound wave RAM + case 0x90 ... 0x9F: + gbc_sound_wave_update = 1; + address8(io_registers, address) = value; + break; + + // Sound FIFO A + case 0xA0: + sound_timer_queue8(0, value); + break; + + // Sound FIFO B + case 0xA4: + sound_timer_queue8(1, value); + break; + + // DMA control (trigger byte) + case 0xBB: + access_register8_low(0xBA); + trigger_dma(0); + break; + + case 0xC7: + access_register8_low(0xC6); + trigger_dma(1); + break; + + case 0xD3: + access_register8_low(0xD2); + trigger_dma(2); + break; + + case 0xDF: + access_register8_low(0xDE); + trigger_dma(3); + break; + + // Timer counts + case 0x100: + access_register8_low(0x100); + count_timer(0); + break; + + case 0x101: + access_register8_high(0x100); + count_timer(0); + break; + + case 0x104: + access_register8_low(0x104); + count_timer(1); + break; + + case 0x105: + access_register8_high(0x104); + count_timer(1); + break; + + case 0x108: + access_register8_low(0x108); + count_timer(2); + break; + + case 0x109: + access_register8_high(0x108); + count_timer(2); + break; + + case 0x10C: + access_register8_low(0x10C); + count_timer(3); + break; + + case 0x10D: + access_register8_high(0x10C); + count_timer(3); + break; + + // Timer control (trigger byte) + case 0x103: + access_register8_low(0x102); + trigger_timer(0); + break; + + case 0x107: + access_register8_low(0x106); + trigger_timer(1); + break; + + case 0x10B: + access_register8_low(0x10A); + trigger_timer(2); + break; + + case 0x10F: + access_register8_low(0x10E); + trigger_timer(3); + break; + + // IF + case 0x202: + address8(io_registers, 0x202) &= ~value; + break; + + case 0x203: + address8(io_registers, 0x203) &= ~value; + break; + + // Halt + case 0x301: + if((value & 0x01) == 0) + reg[CPU_HALT_STATE] = CPU_HALT; + else + reg[CPU_HALT_STATE] = CPU_STOP; + + return CPU_ALERT_HALT; + break; + + default: + address8(io_registers, address) = value; + break; + } + + return CPU_ALERT_NONE; +} + +cpu_alert_type function_cc write_io_register16(u32 address, u32 value) +{ + switch(address) + { + case 0x00: + { + u32 dispcnt = io_registers[REG_DISPCNT]; + if((value & 0x07) != (dispcnt & 0x07)) + oam_update = 1; + + address16(io_registers, 0x00) = value; + break; + } + + // DISPSTAT + case 0x04: + address16(io_registers, 0x04) = + (address16(io_registers, 0x04) & 0x07) | (value & ~0x07); + break; + + // VCOUNT + case 0x06: + break; + + // BG2 reference X + case 0x28: + access_register16_low(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + case 0x2A: + access_register16_high(0x28); + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + // BG2 reference Y + case 0x2C: + access_register16_low(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + case 0x2E: + access_register16_high(0x2C); + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + // BG3 reference X + + case 0x38: + access_register16_low(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + case 0x3A: + access_register16_high(0x38); + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + // BG3 reference Y + case 0x3C: + access_register16_low(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + case 0x3E: + access_register16_high(0x3C); + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + // Sound 1 control sweep + case 0x60: + gbc_sound_tone_control_sweep(); + break; + + // Sound 1 control duty/length/envelope + case 0x62: + gbc_sound_tone_control_low(0, 0x62); + break; + + // Sound 1 control frequency + case 0x64: + gbc_sound_tone_control_high(0, 0x64); + break; + + // Sound 2 control duty/length/envelope + case 0x68: + gbc_sound_tone_control_low(1, 0x68); + break; + + // Sound 2 control frequency + case 0x6C: + gbc_sound_tone_control_high(1, 0x6C); + break; + + // Sound 3 control wave + case 0x70: + gbc_sound_wave_control(); + break; + + // Sound 3 control length/volume + case 0x72: + gbc_sound_tone_control_low_wave(); + break; + + // Sound 3 control frequency + case 0x74: + gbc_sound_tone_control_high_wave(); + break; + + // Sound 4 control length/envelope + case 0x78: + gbc_sound_tone_control_low(3, 0x78); + break; + + // Sound 4 control frequency + case 0x7C: + gbc_sound_noise_control(); + break; + + // Sound control L + case 0x80: + gbc_trigger_sound(); + break; + + // Sound control H + case 0x82: + trigger_sound(); + break; + + // Sound control X + case 0x84: + sound_on(); + break; + + // Sound wave RAM + case 0x90 ... 0x9E: + gbc_sound_wave_update = 1; + address16(io_registers, address) = value; + break; + + // Sound FIFO A + case 0xA0: + sound_timer_queue16(0, value); + break; + + // Sound FIFO B + case 0xA4: + sound_timer_queue16(1, value); + break; + + // DMA control + case 0xBA: + trigger_dma(0); + break; + + case 0xC6: + trigger_dma(1); + break; + + case 0xD2: + trigger_dma(2); + break; + + case 0xDE: + trigger_dma(3); + break; + + // Timer counts + case 0x100: + count_timer(0); + break; + + case 0x104: + count_timer(1); + break; + + case 0x108: + count_timer(2); + break; + + case 0x10C: + count_timer(3); + break; + + // Timer control + case 0x102: + trigger_timer(0); + break; + + case 0x106: + trigger_timer(1); + break; + + case 0x10A: + trigger_timer(2); + break; + + case 0x10E: + trigger_timer(3); + break; + + // P1 + case 0x130: + break; + + // Interrupt flag + case 0x202: + address16(io_registers, 0x202) &= ~value; + break; + + // WAITCNT + case 0x204: + break; + + // Halt + case 0x300: + if(((value >> 8) & 0x01) == 0) + reg[CPU_HALT_STATE] = CPU_HALT; + else + reg[CPU_HALT_STATE] = CPU_STOP; + + return CPU_ALERT_HALT; + + default: + address16(io_registers, address) = value; + break; + } + + return CPU_ALERT_NONE; +} + + +cpu_alert_type function_cc write_io_register32(u32 address, u32 value) +{ + switch(address) + { + // BG2 reference X + case 0x28: + affine_reference_x[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x28) = value; + break; + + // BG2 reference Y + case 0x2C: + affine_reference_y[0] = (s32)(value << 4) >> 4; + address32(io_registers, 0x2C) = value; + break; + + // BG3 reference X + case 0x38: + affine_reference_x[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x38) = value; + break; + + // BG3 reference Y + case 0x3C: + affine_reference_y[1] = (s32)(value << 4) >> 4; + address32(io_registers, 0x3C) = value; + break; + + // Sound FIFO A + case 0xA0: + sound_timer_queue32(0, value); + break; + + // Sound FIFO B + case 0xA4: + sound_timer_queue32(1, value); + break; + + default: + { + cpu_alert_type alert_low = + write_io_register16(address, value & 0xFFFF); + + cpu_alert_type alert_high = + write_io_register16(address + 2, value >> 16); + + if(alert_high) + return alert_high; + + return alert_low; + } + } + + return CPU_ALERT_NONE; +} + +#define write_palette8(address, value) \ + +#define write_palette16(address, value) \ +{ \ + u32 palette_address = address; \ + address16(palette_ram, palette_address) = value; \ + convert_palette(value); \ + address16(palette_ram_converted, palette_address) = value; \ +} \ + +#define write_palette32(address, value) \ +{ \ + u32 palette_address = address; \ + u32 value_high = value >> 16; \ + u32 value_low = value & 0xFFFF; \ + address32(palette_ram, palette_address) = value; \ + convert_palette(value_high); \ + convert_palette(value_low); \ + value = (value_high << 16) | value_low; \ + address32(palette_ram_converted, palette_address) = value; \ +} \ + + +void function_cc write_backup(u32 address, u32 value) +{ + value &= 0xFF; + + if(backup_type == BACKUP_NONE) + backup_type = BACKUP_SRAM; + + + // gamepak SRAM or Flash ROM + if((address == 0x5555) && (flash_mode != FLASH_WRITE_MODE)) + { + if((flash_command_position == 0) && (value == 0xAA)) + { + backup_type = BACKUP_FLASH; + flash_command_position = 1; + } + + if(flash_command_position == 2) + { + switch(value) + { + case 0x90: + // Enter ID mode, this also tells the emulator that we're using + // flash, not SRAM + + if(flash_mode == FLASH_BASE_MODE) + flash_mode = FLASH_ID_MODE; + + break; + + case 0x80: + // Enter erase mode + if(flash_mode == FLASH_BASE_MODE) + flash_mode = FLASH_ERASE_MODE; + break; + + case 0xF0: + // Terminate ID mode + if(flash_mode == FLASH_ID_MODE) + flash_mode = FLASH_BASE_MODE; + break; + + case 0xA0: + // Write mode + if(flash_mode == FLASH_BASE_MODE) + flash_mode = FLASH_WRITE_MODE; + break; + + case 0xB0: + // Bank switch + // Here the chip is now officially 128KB. + flash_size = FLASH_SIZE_128KB; + if(flash_mode == FLASH_BASE_MODE) + flash_mode = FLASH_BANKSWITCH_MODE; + break; + + case 0x10: + // Erase chip + if(flash_mode == FLASH_ERASE_MODE) + { + if(flash_size == FLASH_SIZE_64KB) + memset(gamepak_backup, 0xFF, 1024 * 64); + else + memset(gamepak_backup, 0xFF, 1024 * 128); + backup_update = write_backup_delay; + flash_mode = FLASH_BASE_MODE; + } + break; + + default: + break; + } + flash_command_position = 0; + } + if(backup_type == BACKUP_SRAM) + gamepak_backup[0x5555] = value; + } + else + + if((address == 0x2AAA) && (value == 0x55) && + (flash_command_position == 1)) + { + flash_command_position = 2; + } + else + { + if((flash_command_position == 2) && + (flash_mode == FLASH_ERASE_MODE) && (value == 0x30)) + { + // Erase sector + memset(flash_bank_ptr + (address & 0xF000), 0xFF, 1024 * 4); + backup_update = write_backup_delay; + flash_mode = FLASH_BASE_MODE; + flash_command_position = 0; + } + else + + if((flash_command_position == 0) && + (flash_mode == FLASH_BANKSWITCH_MODE) && (address == 0x0000) && + (flash_size == FLASH_SIZE_128KB)) + { + flash_bank_ptr = gamepak_backup + ((value & 0x01) * (1024 * 64)); + flash_mode = FLASH_BASE_MODE; + } + else + + if((flash_command_position == 0) && (flash_mode == FLASH_WRITE_MODE)) + { + // Write value to flash ROM + backup_update = write_backup_delay; + flash_bank_ptr[address] = value; + flash_mode = FLASH_BASE_MODE; + } + else + + if(backup_type == BACKUP_SRAM) + { + // Write value to SRAM + backup_update = write_backup_delay; + // Hit 64KB territory? + if(address >= 0x8000) + sram_size = SRAM_SIZE_64KB; + gamepak_backup[address] = value; + } + } +} + +#define write_backup8() \ + write_backup(address & 0xFFFF, value) \ + +#define write_backup16() \ + +#define write_backup32() \ + +#define write_vram8() \ + address &= ~0x01; \ + address16(vram, address) = ((value << 8) | value) \ + +#define write_vram16() \ + address16(vram, address) = value \ + +#define write_vram32() \ + address32(vram, address) = value \ + +// RTC code derived from VBA's (due to lack of any real publically available +// documentation...) + +typedef enum +{ + RTC_DISABLED, + RTC_IDLE, + RTC_COMMAND, + RTC_OUTPUT_DATA, + RTC_INPUT_DATA +} rtc_state_type; + +typedef enum +{ + RTC_COMMAND_RESET = 0x60, + RTC_COMMAND_WRITE_STATUS = 0x62, + RTC_COMMAND_READ_STATUS = 0x63, + RTC_COMMAND_OUTPUT_TIME_FULL = 0x65, + RTC_COMMAND_OUTPUT_TIME = 0x67 +} rtc_command_type; + +typedef enum +{ + RTC_WRITE_TIME, + RTC_WRITE_TIME_FULL, + RTC_WRITE_STATUS +} rtc_write_mode_type; + +rtc_state_type rtc_state = RTC_DISABLED; +rtc_write_mode_type rtc_write_mode; +u8 rtc_registers[3]; +u32 rtc_command; +u32 rtc_data[12]; +u32 rtc_status = 0x40; +u32 rtc_data_bytes; +s32 rtc_bit_count; + +u32 encode_bcd(u8 value) +{ + return ((value / 10) << 4) | (value % 10); +} + +#define write_rtc_register(index, _value) \ + update_address = 0x80000C4 + (index * 2); \ + rtc_registers[index] = _value; \ + rtc_page_index = update_address >> 15; \ + map = memory_map_read[rtc_page_index]; \ + \ + if(map == NULL) \ + map = load_gamepak_page(rtc_page_index & 0x3FF); \ + \ + address16(map, update_address & 0x7FFF) = _value \ + +void function_cc write_rtc(u32 address, u32 value) +{ + u32 rtc_page_index; + u32 update_address; + u8 *map; + + value &= 0xFFFF; + + switch(address) + { + // RTC command + // Bit 0: SCHK, perform action + // Bit 1: IO, input/output command data + // Bit 2: CS, select input/output? If high make I/O write only + case 0xC4: + if(rtc_state == RTC_DISABLED) + rtc_state = RTC_IDLE; + if(!(rtc_registers[0] & 0x04)) + value = (rtc_registers[0] & 0x02) | (value & ~0x02); + if(rtc_registers[2] & 0x01) + { + // To begin writing a command 1, 5 must be written to the command + // registers. + if((rtc_state == RTC_IDLE) && (rtc_registers[0] == 0x01) && + (value == 0x05)) + { + // We're now ready to begin receiving a command. + write_rtc_register(0, value); + rtc_state = RTC_COMMAND; + rtc_command = 0; + rtc_bit_count = 7; + } + else + { + write_rtc_register(0, value); + switch(rtc_state) + { + // Accumulate RTC command by receiving the next bit, and if we + // have accumulated enough bits to form a complete command + // execute it. + case RTC_COMMAND: + if(rtc_registers[0] & 0x01) + { + rtc_command |= ((value & 0x02) >> 1) << rtc_bit_count; + rtc_bit_count--; + } + + // Have we received a full RTC command? If so execute it. + if(rtc_bit_count < 0) + { + switch(rtc_command) + { + // Resets RTC + case RTC_COMMAND_RESET: + rtc_state = RTC_IDLE; + memset(rtc_registers, 0, sizeof(rtc_registers)); + break; + + // Sets status of RTC + case RTC_COMMAND_WRITE_STATUS: + rtc_state = RTC_INPUT_DATA; + rtc_data_bytes = 1; + rtc_write_mode = RTC_WRITE_STATUS; + break; + + // Outputs current status of RTC + case RTC_COMMAND_READ_STATUS: + rtc_state = RTC_OUTPUT_DATA; + rtc_data_bytes = 1; + rtc_data[0] = rtc_status; + break; + + // Actually outputs the time, all of it + case RTC_COMMAND_OUTPUT_TIME_FULL: + { + struct tm *current_time; + time_t current_time_flat; + u32 day_of_week; + + time(¤t_time_flat); + current_time = localtime(¤t_time_flat); + + day_of_week = current_time->tm_wday; + if(day_of_week == 0) + day_of_week = 6; + else + day_of_week--; + + rtc_state = RTC_OUTPUT_DATA; + rtc_data_bytes = 7; + rtc_data[0] = encode_bcd(current_time->tm_year % 100); + rtc_data[1] = encode_bcd(current_time->tm_mon + 1); + rtc_data[2] = encode_bcd(current_time->tm_mday); + rtc_data[3] = encode_bcd(day_of_week); + rtc_data[4] = encode_bcd(current_time->tm_hour); + rtc_data[5] = encode_bcd(current_time->tm_min); + rtc_data[6] = encode_bcd(current_time->tm_sec); + + break; + } + + // Only outputs the current time of day. + case RTC_COMMAND_OUTPUT_TIME: + { + struct tm *current_time; + time_t current_time_flat; + + time(¤t_time_flat); + current_time = localtime(¤t_time_flat); + + rtc_state = RTC_OUTPUT_DATA; + rtc_data_bytes = 3; + rtc_data[0] = encode_bcd(current_time->tm_hour); + rtc_data[1] = encode_bcd(current_time->tm_min); + rtc_data[2] = encode_bcd(current_time->tm_sec); + break; + } + } + rtc_bit_count = 0; + } + break; + + // Receive parameters from the game as input to the RTC + // for a given command. Read one bit at a time. + case RTC_INPUT_DATA: + // Bit 1 of parameter A must be high for input + if(rtc_registers[1] & 0x02) + { + // Read next bit for input + if(!(value & 0x01)) + { + rtc_data[rtc_bit_count >> 3] |= + ((value & 0x01) << (7 - (rtc_bit_count & 0x07))); + } + else + { + rtc_bit_count++; + + if(rtc_bit_count == (rtc_data_bytes * 8)) + { + rtc_state = RTC_IDLE; + switch(rtc_write_mode) + { + case RTC_WRITE_STATUS: + rtc_status = rtc_data[0]; + break; + + default: + break; + } + } + } + } + break; + + case RTC_OUTPUT_DATA: + // Bit 1 of parameter A must be low for output + if(!(rtc_registers[1] & 0x02)) + { + // Write next bit to output, on bit 1 of parameter B + if(!(value & 0x01)) + { + u8 current_output_byte = rtc_registers[2]; + + current_output_byte = + (current_output_byte & ~0x02) | + (((rtc_data[rtc_bit_count >> 3] >> + (rtc_bit_count & 0x07)) & 0x01) << 1); + + write_rtc_register(0, current_output_byte); + + } + else + { + rtc_bit_count++; + + if(rtc_bit_count == (rtc_data_bytes * 8)) + { + rtc_state = RTC_IDLE; + memset(rtc_registers, 0, sizeof(rtc_registers)); + } + } + } + break; + + default: + break; + } + } + } + else + { + write_rtc_register(2, value); + } + break; + + // Write parameter A + case 0xC6: + write_rtc_register(1, value); + break; + + // Write parameter B + case 0xC8: + write_rtc_register(2, value); + break; + } +} + +#define write_rtc8() \ + +#define write_rtc16() \ + write_rtc(address & 0xFF, value) \ + +#define write_rtc32() \ + +#define write_memory(type) \ + switch(address >> 24) \ + { \ + case 0x02: \ + /* external work RAM */ \ + address = (address & 0x7FFF) + ((address & 0x38000) * 2) + 0x8000; \ + address##type(ewram, address) = value; \ + break; \ + \ + case 0x03: \ + /* internal work RAM */ \ + address##type(iwram, (address & 0x7FFF) + 0x8000) = value; \ + break; \ + \ + case 0x04: \ + /* I/O registers */ \ + return write_io_register##type(address & 0x3FF, value); \ + \ + case 0x05: \ + /* palette RAM */ \ + write_palette##type(address & 0x3FF, value); \ + break; \ + \ + case 0x06: \ + /* VRAM */ \ + address &= 0x1FFFF; \ + if(address >= 0x18000) \ + address -= 0x8000; \ + \ + write_vram##type(); \ + break; \ + \ + case 0x07: \ + /* OAM RAM */ \ + oam_update = 1; \ + address##type(oam_ram, address & 0x3FF) = value; \ + break; \ + \ + case 0x08: \ + /* gamepak ROM or RTC */ \ + write_rtc##type(); \ + break; \ + \ + case 0x09 ... 0x0C: \ + /* gamepak ROM space */ \ + break; \ + \ + case 0x0D: \ + write_eeprom(address, value); \ + break; \ + \ + case 0x0E: \ + write_backup##type(); \ + break; \ + } \ + +u8 function_cc read_memory8(u32 address) +{ + u8 value; + read_memory(8); + return value; +} + +u16 function_cc read_memory16_signed(u32 address) +{ + u16 value; + + if(address & 0x01) + { + return (s8)read_memory8(address); + } + else + { + read_memory(16); + } + + return value; +} + +// unaligned reads are actually 32bit + +u32 function_cc read_memory16(u32 address) +{ + u32 value; + + if(address & 0x01) + { + address &= ~0x01; + read_memory(16); + ror(value, value, 8); + } + else + { + read_memory(16); + } + + return value; +} + + +u32 function_cc read_memory32(u32 address) +{ + u32 value; + if(address & 0x03) + { + u32 rotate = (address & 0x03) * 8; + address &= ~0x03; + read_memory(32); + ror(value, value, rotate); + } + else + { + read_memory(32); + } + + return value; +} + +cpu_alert_type function_cc write_memory8(u32 address, u8 value) +{ + write_memory(8); + return CPU_ALERT_NONE; +} + +cpu_alert_type function_cc write_memory16(u32 address, u16 value) +{ + write_memory(16); + return CPU_ALERT_NONE; +} + +cpu_alert_type function_cc write_memory32(u32 address, u32 value) +{ + write_memory(32); + return CPU_ALERT_NONE; +} + +char backup_filename[512]; + +u32 load_backup(char *name) +{ + file_open(backup_file, name, read); + + if(file_check_valid(backup_file)) + { + u32 backup_size = file_length(name, backup_file); + + file_read(backup_file, gamepak_backup, backup_size); + + file_close(backup_file); + + // The size might give away what kind of backup it is. + switch(backup_size) + { + case 0x200: + backup_type = BACKUP_EEPROM; + eeprom_size = EEPROM_512_BYTE; + break; + + case 0x2000: + backup_type = BACKUP_EEPROM; + eeprom_size = EEPROM_8_KBYTE; + break; + + case 0x8000: + backup_type = BACKUP_SRAM; + sram_size = SRAM_SIZE_32KB; + break; + + // Could be either flash or SRAM, go with flash + case 0x10000: + backup_type = BACKUP_FLASH; + sram_size = FLASH_SIZE_64KB; + break; + + case 0x20000: + backup_type = BACKUP_FLASH; + flash_size = FLASH_SIZE_128KB; + break; + } + return 1; + } + else + { + backup_type = BACKUP_NONE; + memset(gamepak_backup, 0xFF, 1024 * 128); + } + + return 0; +} + +u32 save_backup(char *name) +{ + if(backup_type != BACKUP_NONE) + { + file_open(backup_file, name, write); + + if(file_check_valid(backup_file)) + { + u32 backup_size = 0; + + switch(backup_type) + { + case BACKUP_SRAM: + if(sram_size == SRAM_SIZE_32KB) + backup_size = 0x8000; + else + backup_size = 0x10000; + break; + + case BACKUP_FLASH: + if(flash_size == FLASH_SIZE_64KB) + backup_size = 0x10000; + else + backup_size = 0x20000; + break; + + case BACKUP_EEPROM: + if(eeprom_size == EEPROM_512_BYTE) + backup_size = 0x200; + else + backup_size = 0x2000; + break; + + default: + break; + } + + file_write(backup_file, gamepak_backup, backup_size); + + file_close(backup_file); + return 1; + } + } + + return 0; +} + +void update_backup() +{ + if(backup_update != (write_backup_delay + 1)) + backup_update--; + + if(backup_update == 0) + { + save_backup(backup_filename); + backup_update = write_backup_delay + 1; + } +} + +void update_backup_force() +{ + save_backup(backup_filename); +} + +#define CONFIG_FILENAME "game_config.txt" + +char *skip_spaces(char *line_ptr) +{ + while(*line_ptr == ' ') + line_ptr++; + + return line_ptr; +} + +s32 parse_config_line(char *current_line, char *current_variable, char *current_value) +{ + char *line_ptr = current_line; + char *line_ptr_new; + + if((current_line[0] == 0) || (current_line[0] == '#')) + return -1; + + line_ptr_new = strchr(line_ptr, ' '); + if(line_ptr_new == NULL) + return -1; + + *line_ptr_new = 0; + strcpy(current_variable, line_ptr); + line_ptr_new = skip_spaces(line_ptr_new + 1); + + if(*line_ptr_new != '=') + return -1; + + line_ptr_new = skip_spaces(line_ptr_new + 1); + strcpy(current_value, line_ptr_new); + line_ptr_new = current_value + strlen(current_value) - 1; + if(*line_ptr_new == '\n') + { + line_ptr_new--; + *line_ptr_new = 0; + } + + if(*line_ptr_new == '\r') + *line_ptr_new = 0; + + return 0; +} + +s32 load_game_config(char *gamepak_title, char *gamepak_code, char *gamepak_maker) +{ + char current_line[256]; + char current_variable[256]; + char current_value[256]; + char config_path[512]; + FILE *config_file; + + idle_loop_target_pc = 0xFFFFFFFF; + iwram_stack_optimize = 1; + bios_rom[0x39] = 0x00; + bios_rom[0x2C] = 0x00; + translation_gate_targets = 0; + flash_device_id = FLASH_DEVICE_MACRONIX_64KB; + + sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, CONFIG_FILENAME); + + config_file = fopen(config_path, "rb"); + + if(config_file) + { + while(fgets(current_line, 256, config_file)) + { + if(parse_config_line(current_line, current_variable, current_value) + != -1) + { + if(strcmp(current_variable, "game_name") || + strcmp(current_value, gamepak_title)) + continue; + + if(!fgets(current_line, 256, config_file) || + (parse_config_line(current_line, current_variable, + current_value) == -1) || + strcmp(current_variable, "game_code") || + strcmp(current_value, gamepak_code)) + continue; + + if(!fgets(current_line, 256, config_file) || + (parse_config_line(current_line, current_variable, + current_value) == -1) || + strcmp(current_variable, "vender_code") || + strcmp(current_value, gamepak_maker)) + continue; + + while(fgets(current_line, 256, config_file)) + { + if(parse_config_line(current_line, current_variable, current_value) + != -1) + { + if(!strcmp(current_variable, "game_name")) + { + fclose(config_file); + return 0; + } + + if(!strcmp(current_variable, "idle_loop_eliminate_target")) + idle_loop_target_pc = strtol(current_value, NULL, 16); + + if(!strcmp(current_variable, "translation_gate_target")) + { + if(translation_gate_targets < MAX_TRANSLATION_GATES) + { + translation_gate_target_pc[translation_gate_targets] = + strtol(current_value, NULL, 16); + translation_gate_targets++; + } + } + + if(!strcmp(current_variable, "iwram_stack_optimize") && + !strcmp(current_value, "no\0")) /* \0 for broken toolchain workaround */ + { + iwram_stack_optimize = 0; + } + + if(!strcmp(current_variable, "flash_rom_type") && + !strcmp(current_value, "128KB")) + { + flash_device_id = FLASH_DEVICE_MACRONIX_128KB; + } + + if(!strcmp(current_variable, "bios_rom_hack_39") && + !strcmp(current_value, "yes")) + { + bios_rom[0x39] = 0xC0; + } + + if(!strcmp(current_variable, "bios_rom_hack_2C") && + !strcmp(current_value, "yes")) + { + bios_rom[0x2C] = 0x02; + } + } + } + + fclose(config_file); + return 0; + } + } + + fclose(config_file); + } + +#ifndef PSP_BUILD + printf("game config missing\n"); +#endif + return -1; +} + +s32 load_gamepak_raw(const char *name) +{ + file_open(gamepak_file, name, read); + + if(file_check_valid(gamepak_file)) + { + u32 file_size = file_length(name, gamepak_file); + + // First, close the last one if it was open, we won't + // be needing it anymore. + if(file_check_valid(gamepak_file_large)) + file_close(gamepak_file_large); + + // If it's a big file size keep it, don't close it, we'll + // probably want to load from it more later. + if(file_size <= gamepak_ram_buffer_size) + { + file_read(gamepak_file, gamepak_rom, file_size); + + file_close(gamepak_file); + +#ifdef PSP_BUILD + gamepak_file_large = -1; +#else + gamepak_file_large = NULL; +#endif + } + else + { + // Read in just enough for the header + file_read(gamepak_file, gamepak_rom, 0x100); + gamepak_file_large = gamepak_file; + } + + return file_size; + } + + return -1; +} + +char gamepak_title[13]; +char gamepak_code[5]; +char gamepak_maker[3]; +char gamepak_filename[512]; + +u32 load_gamepak(const char *name) +{ + char cheats_filename[256]; + + s32 file_size = load_gamepak_raw(name); + + // A dumb April fool's joke was here once :o + + if(file_size != -1) + { + gamepak_size = (file_size + 0x7FFF) & ~0x7FFF; + + strncpy(gamepak_filename, name, sizeof(gamepak_filename)); + gamepak_filename[sizeof(gamepak_filename) - 1] = 0; + + make_rpath(backup_filename, sizeof(backup_filename), ".sav"); + if (!load_backup(backup_filename)) + { + // try path used by older versions + strcpy(backup_filename, name); + change_ext(gamepak_filename, backup_filename, ".sav"); + load_backup(backup_filename); + } + + memcpy(gamepak_title, gamepak_rom + 0xA0, 12); + memcpy(gamepak_code, gamepak_rom + 0xAC, 4); + memcpy(gamepak_maker, gamepak_rom + 0xB0, 2); + gamepak_title[12] = 0; + gamepak_code[4] = 0; + gamepak_maker[2] = 0; + + load_game_config(gamepak_title, gamepak_code, gamepak_maker); +#ifndef __LIBRETRO__ + load_game_config_file(); +#endif + + change_ext(gamepak_filename, cheats_filename, ".cht"); + add_cheats(cheats_filename); + + return 0; + } + + return -1; +} + +s32 load_bios(char *name) +{ + file_open(bios_file, name, read); + + if(file_check_valid(bios_file)) + { + file_read(bios_file, bios_rom, 0x4000); + + // This is a hack to get Zelda working, because emulating + // the proper memory read behavior here is much too expensive. + file_close(bios_file); + return 0; + } + + return -1; +} + +// DMA memory regions can be one of the following: +// IWRAM - 32kb offset from the contiguous iwram region. +// EWRAM - like segmented but with self modifying code check. +// VRAM - 96kb offset from the contiguous vram region, should take care +// Palette RAM - Converts palette entries when written to. +// OAM RAM - Sets OAM modified flag to true. +// I/O registers - Uses the I/O register function. +// of mirroring properly. +// Segmented RAM/ROM - a region >= 32kb, the translated address has to +// be reloaded if it wraps around the limit (cartride ROM) +// Ext - should be handled by the memory read/write function. + +// The following map determines the region of each (assumes DMA access +// is not done out of bounds) + +typedef enum +{ + DMA_REGION_IWRAM, + DMA_REGION_EWRAM, + DMA_REGION_VRAM, + DMA_REGION_PALETTE_RAM, + DMA_REGION_OAM_RAM, + DMA_REGION_IO, + DMA_REGION_GAMEPAK, + DMA_REGION_EXT, + DMA_REGION_BIOS, + DMA_REGION_NULL +} dma_region_type; + +dma_region_type dma_region_map[16] = +{ + DMA_REGION_BIOS, // 0x00 - BIOS + DMA_REGION_NULL, // 0x01 - Nothing + DMA_REGION_EWRAM, // 0x02 - EWRAM + DMA_REGION_IWRAM, // 0x03 - IWRAM + DMA_REGION_IO, // 0x04 - I/O registers + DMA_REGION_PALETTE_RAM, // 0x05 - palette RAM + DMA_REGION_VRAM, // 0x06 - VRAM + DMA_REGION_OAM_RAM, // 0x07 - OAM RAM + DMA_REGION_GAMEPAK, // 0x08 - gamepak ROM + DMA_REGION_GAMEPAK, // 0x09 - gamepak ROM + DMA_REGION_GAMEPAK, // 0x0A - gamepak ROM + DMA_REGION_GAMEPAK, // 0x0B - gamepak ROM + DMA_REGION_GAMEPAK, // 0x0C - gamepak ROM + DMA_REGION_EXT, // 0x0D - EEPROM + DMA_REGION_EXT, // 0x0E - gamepak SRAM/flash ROM + DMA_REGION_EXT // 0x0F - gamepak SRAM/flash ROM +}; + +#define dma_adjust_ptr_inc(ptr, size) \ + ptr += (size / 8) \ + +#define dma_adjust_ptr_dec(ptr, size) \ + ptr -= (size / 8) \ + +#define dma_adjust_ptr_fix(ptr, size) \ + +#define dma_adjust_ptr_writeback() \ + dma->dest_address = dest_ptr \ + +#define dma_adjust_ptr_reload() \ + +#define dma_print(src_op, dest_op, transfer_size, wb) \ + printf("dma from %x (%s) to %x (%s) for %x (%s) (%s) (%d) (pc %x)\n", \ + src_ptr, #src_op, dest_ptr, #dest_op, length, #transfer_size, #wb, \ + dma->irq, reg[15]); \ + +#define dma_smc_vars_src() \ + +#define dma_smc_vars_dest() \ + u32 smc_trigger = 0 \ + +#define dma_vars_iwram(type) \ + dma_smc_vars_##type() \ + +#define dma_vars_vram(type) \ + +#define dma_vars_palette_ram(type) \ + +#define dma_oam_ram_src() \ + +#define dma_oam_ram_dest() \ + oam_update = 1 \ + +#define dma_vars_oam_ram(type) \ + dma_oam_ram_##type() \ + +#define dma_vars_io(type) \ + +#define dma_segmented_load_src() \ + memory_map_read[src_current_region] \ + +#define dma_segmented_load_dest() \ + memory_map_write[dest_current_region] \ + +#define dma_vars_gamepak(type) \ + u32 type##_new_region; \ + u32 type##_current_region = type##_ptr >> 15; \ + u8 *type##_address_block = dma_segmented_load_##type(); \ + if(type##_address_block == NULL) \ + { \ + if((type##_ptr & 0x1FFFFFF) >= gamepak_size) \ + break; \ + type##_address_block = load_gamepak_page(type##_current_region & 0x3FF); \ + } \ + +#define dma_vars_ewram(type) \ + dma_smc_vars_##type(); \ + u32 type##_new_region; \ + u32 type##_current_region = type##_ptr >> 15; \ + u8 *type##_address_block = dma_segmented_load_##type() \ + +#define dma_vars_bios(type) \ + +#define dma_vars_ext(type) \ + +#define dma_ewram_check_region(type) \ + type##_new_region = (type##_ptr >> 15); \ + if(type##_new_region != type##_current_region) \ + { \ + type##_current_region = type##_new_region; \ + type##_address_block = dma_segmented_load_##type(); \ + } \ + +#define dma_gamepak_check_region(type) \ + type##_new_region = (type##_ptr >> 15); \ + if(type##_new_region != type##_current_region) \ + { \ + type##_current_region = type##_new_region; \ + type##_address_block = dma_segmented_load_##type(); \ + if(type##_address_block == NULL) \ + { \ + type##_address_block = \ + load_gamepak_page(type##_current_region & 0x3FF); \ + } \ + } \ + +#define dma_read_iwram(type, transfer_size) \ + read_value = address##transfer_size(iwram + 0x8000, type##_ptr & 0x7FFF) \ + +#define dma_read_vram(type, transfer_size) \ + read_value = address##transfer_size(vram, type##_ptr & 0x1FFFF) \ + +#define dma_read_io(type, transfer_size) \ + read_value = address##transfer_size(io_registers, type##_ptr & 0x7FFF) \ + +#define dma_read_oam_ram(type, transfer_size) \ + read_value = address##transfer_size(oam_ram, type##_ptr & 0x3FF) \ + +#define dma_read_palette_ram(type, transfer_size) \ + read_value = address##transfer_size(palette_ram, type##_ptr & 0x3FF) \ + +#define dma_read_ewram(type, transfer_size) \ + dma_ewram_check_region(type); \ + read_value = address##transfer_size(type##_address_block, \ + type##_ptr & 0x7FFF) \ + +#define dma_read_gamepak(type, transfer_size) \ + dma_gamepak_check_region(type); \ + read_value = address##transfer_size(type##_address_block, \ + type##_ptr & 0x7FFF) \ + +// DMAing from the BIOS is funny, just returns 0.. + +#define dma_read_bios(type, transfer_size) \ + read_value = 0 \ + +#define dma_read_ext(type, transfer_size) \ + read_value = read_memory##transfer_size(type##_ptr) \ + +#define dma_write_iwram(type, transfer_size) \ + address##transfer_size(iwram + 0x8000, type##_ptr & 0x7FFF) = read_value; \ + smc_trigger |= address##transfer_size(iwram, type##_ptr & 0x7FFF) \ + +#define dma_write_vram(type, transfer_size) \ + address##transfer_size(vram, type##_ptr & 0x1FFFF) = read_value \ + +#define dma_write_io(type, transfer_size) \ + write_io_register##transfer_size(type##_ptr & 0x3FF, read_value) \ + +#define dma_write_oam_ram(type, transfer_size) \ + address##transfer_size(oam_ram, type##_ptr & 0x3FF) = read_value \ + +#define dma_write_palette_ram(type, transfer_size) \ + write_palette##transfer_size(type##_ptr & 0x3FF, read_value) \ + +#define dma_write_ext(type, transfer_size) \ + write_memory##transfer_size(type##_ptr, read_value) \ + +#define dma_write_ewram(type, transfer_size) \ + dma_ewram_check_region(type); \ + \ + address##transfer_size(type##_address_block, type##_ptr & 0x7FFF) = \ + read_value; \ + smc_trigger |= address##transfer_size(type##_address_block, \ + (type##_ptr & 0x7FFF) - 0x8000) \ + +#define dma_epilogue_iwram() \ + if(smc_trigger) \ + { \ + /* Special return code indicating to retranslate to the CPU code */ \ + return_value = CPU_ALERT_SMC; \ + } \ + +#define dma_epilogue_ewram() \ + if(smc_trigger) \ + { \ + /* Special return code indicating to retranslate to the CPU code */ \ + return_value = CPU_ALERT_SMC; \ + } \ + +#define dma_epilogue_vram() \ + +#define dma_epilogue_io() \ + +#define dma_epilogue_oam_ram() \ + +#define dma_epilogue_palette_ram() \ + +#define dma_epilogue_GAMEPAK() \ + +#define dma_epilogue_ext() \ + +#define print_line() \ + dma_print(src_op, dest_op, transfer_size, wb); \ + +#define dma_transfer_loop_region(src_region_type, dest_region_type, src_op, \ + dest_op, transfer_size, wb) \ +{ \ + dma_vars_##src_region_type(src); \ + dma_vars_##dest_region_type(dest); \ + \ + for(i = 0; i < length; i++) \ + { \ + dma_read_##src_region_type(src, transfer_size); \ + dma_write_##dest_region_type(dest, transfer_size); \ + dma_adjust_ptr_##src_op(src_ptr, transfer_size); \ + dma_adjust_ptr_##dest_op(dest_ptr, transfer_size); \ + } \ + dma->source_address = src_ptr; \ + dma_adjust_ptr_##wb(); \ + dma_epilogue_##dest_region_type(); \ + break; \ +} \ + +#define dma_transfer_loop(src_op, dest_op, transfer_size, wb); \ +{ \ + u32 src_region = src_ptr >> 24; \ + u32 dest_region = dest_ptr >> 24; \ + dma_region_type src_region_type = dma_region_map[src_region]; \ + dma_region_type dest_region_type = dma_region_map[dest_region]; \ + \ + switch(src_region_type | (dest_region_type << 4)) \ + { \ + case (DMA_REGION_BIOS | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(bios, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(iwram, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(ewram, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(vram, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(palette_ram, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(oam_ram, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(io, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(gamepak, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_IWRAM << 4)): \ + dma_transfer_loop_region(ext, iwram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(bios, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(iwram, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(ewram, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(vram, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(palette_ram, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(oam_ram, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(io, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(gamepak, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_EWRAM << 4)): \ + dma_transfer_loop_region(ext, ewram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(bios, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(iwram, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(ewram, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(vram, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(palette_ram, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(oam_ram, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(io, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(gamepak, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_VRAM << 4)): \ + dma_transfer_loop_region(ext, vram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(bios, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(iwram, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(ewram, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(vram, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(palette_ram, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(oam_ram, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(io, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(gamepak, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_PALETTE_RAM << 4)): \ + dma_transfer_loop_region(ext, palette_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(bios, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(iwram, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(ewram, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(vram, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(palette_ram, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(oam_ram, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(io, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(gamepak, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_OAM_RAM << 4)): \ + dma_transfer_loop_region(ext, oam_ram, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(bios, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(iwram, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(ewram, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(vram, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(palette_ram, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(oam_ram, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(io, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(gamepak, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_IO << 4)): \ + dma_transfer_loop_region(ext, io, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_BIOS | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(bios, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IWRAM | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(iwram, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EWRAM | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(ewram, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_VRAM | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(vram, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_PALETTE_RAM | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(palette_ram, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_OAM_RAM | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(oam_ram, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_IO | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(io, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_GAMEPAK | (DMA_REGION_EXT << 4)): \ + dma_transfer_loop_region(gamepak, ext, src_op, dest_op, \ + transfer_size, wb); \ + \ + case (DMA_REGION_EXT | (DMA_REGION_EXT << 3)): \ + dma_transfer_loop_region(ext, ext, src_op, dest_op, \ + transfer_size, wb); \ + } \ + break; \ +} \ + +#define dma_transfer_expand(transfer_size) \ + switch((dma->dest_direction << 2) | dma->source_direction) \ + { \ + case 0x00: \ + dma_transfer_loop(inc, inc, transfer_size, writeback); \ + \ + case 0x01: \ + dma_transfer_loop(dec, inc, transfer_size, writeback); \ + \ + case 0x02: \ + dma_transfer_loop(fix, inc, transfer_size, writeback); \ + \ + case 0x03: \ + break; \ + \ + case 0x04: \ + dma_transfer_loop(inc, dec, transfer_size, writeback); \ + \ + case 0x05: \ + dma_transfer_loop(dec, dec, transfer_size, writeback); \ + \ + case 0x06: \ + dma_transfer_loop(fix, dec, transfer_size, writeback); \ + \ + case 0x07: \ + break; \ + \ + case 0x08: \ + dma_transfer_loop(inc, fix, transfer_size, writeback); \ + \ + case 0x09: \ + dma_transfer_loop(dec, fix, transfer_size, writeback); \ + \ + case 0x0A: \ + dma_transfer_loop(fix, fix, transfer_size, writeback); \ + \ + case 0x0B: \ + break; \ + \ + case 0x0C: \ + dma_transfer_loop(inc, inc, transfer_size, reload); \ + \ + case 0x0D: \ + dma_transfer_loop(dec, inc, transfer_size, reload); \ + \ + case 0x0E: \ + dma_transfer_loop(fix, inc, transfer_size, reload); \ + \ + case 0x0F: \ + break; \ + } \ + +cpu_alert_type dma_transfer(dma_transfer_type *dma) +{ + u32 i; + u32 length = dma->length; + u32 read_value; + u32 src_ptr = dma->source_address; + u32 dest_ptr = dma->dest_address; + cpu_alert_type return_value = CPU_ALERT_NONE; + + // Technically this should be done for source and destination, but + // chances are this is only ever used (probably mistakingly!) for dest. + // The only game I know of that requires this is Lucky Luke. + if((dest_ptr >> 24) != ((dest_ptr + length - 1) >> 24)) + { + u32 first_length = ((dest_ptr & 0xFF000000) + 0x1000000) - dest_ptr; + u32 second_length = length - first_length; + dma->length = first_length; + + dma_transfer(dma); + + dma->length = length; + + length = second_length; + dest_ptr += first_length; + src_ptr += first_length; + } + + if(dma->length_type == DMA_16BIT) + { + src_ptr &= ~0x01; + dest_ptr &= ~0x01; + cycle_dma16_words += length; + dma_transfer_expand(16); + } + else + { + src_ptr &= ~0x03; + dest_ptr &= ~0x03; + cycle_dma32_words += length; + dma_transfer_expand(32); + } + + if((dma->repeat_type == DMA_NO_REPEAT) || + (dma->start_type == DMA_START_IMMEDIATELY)) + { + dma->start_type = DMA_INACTIVE; + address16(io_registers, (dma->dma_channel * 12) + 0xBA) &= + (~0x8000); + } + + if(dma->irq) + { + raise_interrupt(IRQ_DMA0 << dma->dma_channel); + return_value = CPU_ALERT_IRQ; + } + + return return_value; +} + +// Be sure to do this after loading ROMs. + +#define map_region(type, start, end, mirror_blocks, region) \ + for(map_offset = (start) / 0x8000; map_offset < \ + ((end) / 0x8000); map_offset++) \ + { \ + memory_map_##type[map_offset] = \ + ((u8 *)region) + ((map_offset % mirror_blocks) * 0x8000); \ + } \ + +#define map_null(type, start, end) \ + for(map_offset = start / 0x8000; map_offset < (end / 0x8000); \ + map_offset++) \ + { \ + memory_map_##type[map_offset] = NULL; \ + } \ + +#define map_ram_region(type, start, end, mirror_blocks, region) \ + for(map_offset = (start) / 0x8000; map_offset < \ + ((end) / 0x8000); map_offset++) \ + { \ + memory_map_##type[map_offset] = \ + ((u8 *)region) + ((map_offset % mirror_blocks) * 0x10000) + 0x8000; \ + } \ + +#define map_vram(type) \ + for(map_offset = 0x6000000 / 0x8000; map_offset < (0x7000000 / 0x8000); \ + map_offset += 4) \ + { \ + memory_map_##type[map_offset] = vram; \ + memory_map_##type[map_offset + 1] = vram + 0x8000; \ + memory_map_##type[map_offset + 2] = vram + (0x8000 * 2); \ + memory_map_##type[map_offset + 3] = vram + (0x8000 * 2); \ + } \ + +#define map_vram_firstpage(type) \ + for(map_offset = 0x6000000 / 0x8000; map_offset < (0x7000000 / 0x8000); \ + map_offset += 4) \ + { \ + memory_map_##type[map_offset] = vram; \ + memory_map_##type[map_offset + 1] = NULL; \ + memory_map_##type[map_offset + 2] = NULL; \ + memory_map_##type[map_offset + 3] = NULL; \ + } \ + + +// Picks a page to evict +u32 page_time = 0; + +u32 evict_gamepak_page() +{ + // Find the one with the smallest frame timestamp + u32 page_index = 0; + u32 physical_index; + u32 smallest = gamepak_memory_map[0].page_timestamp; + u32 i; + + for(i = 1; i < gamepak_ram_pages; i++) + { + if(gamepak_memory_map[i].page_timestamp <= smallest) + { + smallest = gamepak_memory_map[i].page_timestamp; + page_index = i; + } + } + + physical_index = gamepak_memory_map[page_index].physical_index; + + memory_map_read[(0x8000000 / (32 * 1024)) + physical_index] = NULL; + memory_map_read[(0xA000000 / (32 * 1024)) + physical_index] = NULL; + memory_map_read[(0xC000000 / (32 * 1024)) + physical_index] = NULL; + + return page_index; +} + +u8 *load_gamepak_page(u32 physical_index) +{ + if(physical_index >= (gamepak_size >> 15)) + return gamepak_rom; + + u32 page_index = evict_gamepak_page(); + u32 page_offset = page_index * (32 * 1024); + u8 *swap_location = gamepak_rom + page_offset; + + gamepak_memory_map[page_index].page_timestamp = page_time; + gamepak_memory_map[page_index].physical_index = physical_index; + page_time++; + + file_seek(gamepak_file_large, physical_index * (32 * 1024), SEEK_SET); + file_read(gamepak_file_large, swap_location, (32 * 1024)); + memory_map_read[(0x8000000 / (32 * 1024)) + physical_index] = swap_location; + memory_map_read[(0xA000000 / (32 * 1024)) + physical_index] = swap_location; + memory_map_read[(0xC000000 / (32 * 1024)) + physical_index] = swap_location; + + // If RTC is active page the RTC register bytes so they can be read + if((rtc_state != RTC_DISABLED) && (physical_index == 0)) + { + memcpy(swap_location + 0xC4, rtc_registers, sizeof(rtc_registers)); + } + + return swap_location; +} + +void init_memory_gamepak() +{ + u32 map_offset = 0; + + if(gamepak_size > gamepak_ram_buffer_size) + { + // Large ROMs get special treatment because they + // can't fit into the 16MB ROM buffer. + u32 i; + for(i = 0; i < gamepak_ram_pages; i++) + { + gamepak_memory_map[i].page_timestamp = 0; + gamepak_memory_map[i].physical_index = 0; + } + + map_null(read, 0x8000000, 0xD000000); + } + else + { + map_region(read, 0x8000000, 0x8000000 + gamepak_size, 1024, gamepak_rom); + map_null(read, 0x8000000 + gamepak_size, 0xA000000); + map_region(read, 0xA000000, 0xA000000 + gamepak_size, 1024, gamepak_rom); + map_null(read, 0xA000000 + gamepak_size, 0xC000000); + map_region(read, 0xC000000, 0xC000000 + gamepak_size, 1024, gamepak_rom); + map_null(read, 0xC000000 + gamepak_size, 0xE000000); + } +} + +void init_gamepak_buffer() +{ + // Try to initialize 32MB (this is mainly for non-PSP platforms) + gamepak_rom = NULL; + + gamepak_ram_buffer_size = 32 * 1024 * 1024; + gamepak_rom = malloc(gamepak_ram_buffer_size); + + if(gamepak_rom == NULL) + { + // Try 16MB, for PSP, then lower in 2MB increments + gamepak_ram_buffer_size = 16 * 1024 * 1024; + gamepak_rom = malloc(gamepak_ram_buffer_size); + + while(gamepak_rom == NULL) + { + gamepak_ram_buffer_size -= (2 * 1024 * 1024); + gamepak_rom = malloc(gamepak_ram_buffer_size); + } + } + + // Here's assuming we'll have enough memory left over for this, + // and that the above succeeded (if not we're in trouble all around) + gamepak_ram_pages = gamepak_ram_buffer_size / (32 * 1024); + gamepak_memory_map = malloc(sizeof(gamepak_swap_entry_type) * + gamepak_ram_pages); +} + +void init_memory() +{ + u32 map_offset = 0; + + memory_regions[0x00] = (u8 *)bios_rom; + memory_regions[0x01] = (u8 *)bios_rom; + memory_regions[0x02] = (u8 *)ewram; + memory_regions[0x03] = (u8 *)iwram + 0x8000; + memory_regions[0x04] = (u8 *)io_registers; + memory_regions[0x05] = (u8 *)palette_ram; + memory_regions[0x06] = (u8 *)vram; + memory_regions[0x07] = (u8 *)oam_ram; + memory_regions[0x08] = (u8 *)gamepak_rom; + memory_regions[0x09] = (u8 *)(gamepak_rom + 0xFFFFFF); + memory_regions[0x0A] = (u8 *)gamepak_rom; + memory_regions[0x0B] = (u8 *)(gamepak_rom + 0xFFFFFF); + memory_regions[0x0C] = (u8 *)gamepak_rom; + memory_regions[0x0D] = (u8 *)(gamepak_rom + 0xFFFFFF); + memory_regions[0x0E] = (u8 *)gamepak_backup; + + memory_limits[0x00] = 0x3FFF; + memory_limits[0x01] = 0x3FFF; + memory_limits[0x02] = 0x3FFFF; + memory_limits[0x03] = 0x7FFF; + memory_limits[0x04] = 0x7FFF; + memory_limits[0x05] = 0x3FF; + memory_limits[0x06] = 0x17FFF; + memory_limits[0x07] = 0x3FF; + memory_limits[0x08] = 0x1FFFFFF; + memory_limits[0x09] = 0x1FFFFFF; + memory_limits[0x0A] = 0x1FFFFFF; + memory_limits[0x0B] = 0x1FFFFFF; + memory_limits[0x0C] = 0x1FFFFFF; + memory_limits[0x0D] = 0x1FFFFFF; + memory_limits[0x0E] = 0xFFFF; + + // Fill memory map regions, areas marked as NULL must be checked directly + map_region(read, 0x0000000, 0x1000000, 1, bios_rom); + map_null(read, 0x1000000, 0x2000000); + map_ram_region(read, 0x2000000, 0x3000000, 8, ewram); + map_ram_region(read, 0x3000000, 0x4000000, 1, iwram); + map_region(read, 0x4000000, 0x5000000, 1, io_registers); + map_null(read, 0x5000000, 0x6000000); + map_null(read, 0x6000000, 0x7000000); + map_vram(read); + map_null(read, 0x7000000, 0x8000000); + init_memory_gamepak(); + map_null(read, 0xE000000, 0x10000000); + + // Fill memory map regions, areas marked as NULL must be checked directly + map_null(write, 0x0000000, 0x2000000); + map_ram_region(write, 0x2000000, 0x3000000, 8, ewram); + map_ram_region(write, 0x3000000, 0x4000000, 1, iwram); + map_null(write, 0x4000000, 0x5000000); + map_null(write, 0x5000000, 0x6000000); + + // The problem here is that the current method of handling self-modifying code + // requires writeable memory to be proceeded by 32KB SMC data areas or be + // indirectly writeable. It's possible to get around this if you turn off the SMC + // check altogether, but this will make a good number of ROMs crash (perhaps most + // of the ones that actually need it? This has yet to be determined). + + // This is because VRAM cannot be efficiently made incontiguous, and still allow + // the renderer to work as efficiently. It would, at the very least, require a + // lot of hacking of the renderer which I'm not prepared to do. + + // However, it IS possible to directly map the first page no matter what because + // there's 32kb of blank stuff sitting beneath it. + if(direct_map_vram) + { + map_vram(write); + } + else + { + map_null(write, 0x6000000, 0x7000000); + } + + map_null(write, 0x7000000, 0x8000000); + map_null(write, 0x8000000, 0xE000000); + map_null(write, 0xE000000, 0x10000000); + + memset(io_registers, 0, 0x8000); + memset(oam_ram, 0, 0x400); + memset(palette_ram, 0, 0x400); + memset(iwram, 0, 0x10000); + memset(ewram, 0, 0x80000); + memset(vram, 0, 0x18000); + + io_registers[REG_DISPCNT] = 0x80; + io_registers[REG_P1] = 0x3FF; + io_registers[REG_BG2PA] = 0x100; + io_registers[REG_BG2PD] = 0x100; + io_registers[REG_BG3PA] = 0x100; + io_registers[REG_BG3PD] = 0x100; + io_registers[REG_RCNT] = 0x8000; + + backup_type = BACKUP_NONE; + + sram_size = SRAM_SIZE_32KB; + flash_size = FLASH_SIZE_64KB; + + flash_bank_ptr = gamepak_backup; + flash_command_position = 0; + eeprom_size = EEPROM_512_BYTE; + eeprom_mode = EEPROM_BASE_MODE; + eeprom_address = 0; + eeprom_counter = 0; + + flash_mode = FLASH_BASE_MODE; + + rtc_state = RTC_DISABLED; + memset(rtc_registers, 0, sizeof(rtc_registers)); + bios_read_protect = 0xe129f000; +} + +void memory_term(void) +{ + if (file_check_valid(gamepak_file_large)) + { + file_close(gamepak_file_large); + } + + if (gamepak_memory_map != NULL) + { + free(gamepak_memory_map); + gamepak_memory_map = NULL; + } + + if (gamepak_rom != NULL) + { + free(gamepak_rom); + gamepak_rom = NULL; + } +} + +void bios_region_read_allow() +{ + memory_map_read[0] = bios_rom; +} + +void bios_region_read_protect() +{ + memory_map_read[0] = NULL; +} + + +#define savestate_block(type) \ + cpu_##type##_savestate(savestate_file); \ + input_##type##_savestate(savestate_file); \ + main_##type##_savestate(savestate_file); \ + memory_##type##_savestate(savestate_file); \ + sound_##type##_savestate(savestate_file); \ + video_##type##_savestate(savestate_file) \ + +void gba_load_state(char *savestate_filename) +{ + file_open(savestate_file, savestate_filename, read); + if(file_check_valid(savestate_file)) + { + char current_gamepak_filename[512]; + u32 i; + u32 current_color; + + file_seek(savestate_file, (240 * 160 * 2) + sizeof(time_t), SEEK_SET); + + strcpy(current_gamepak_filename, gamepak_filename); + + savestate_block(read); + + file_close(savestate_file); + + flush_translation_cache_ram(); + flush_translation_cache_rom(); + flush_translation_cache_bios(); + + oam_update = 1; + gbc_sound_update = 1; + if(strcmp(current_gamepak_filename, gamepak_filename)) + { + u32 dot_position = strcspn(current_gamepak_filename, "."); + + // We'll let it slide if the filenames of the savestate and + // the gamepak are similar enough. + strcpy(gamepak_filename, current_gamepak_filename); + if(strncmp(savestate_filename, current_gamepak_filename, dot_position)) + { + if(load_gamepak(gamepak_filename) != -1) + { + reset_gba(); + // Okay, so this takes a while, but for now it works. + gba_load_state(savestate_filename); + } + else + { + quit(); + } + + return; + } + } + + for(i = 0; i < 512; i++) + { + current_color = palette_ram[i]; + palette_ram_converted[i] = + convert_palette(current_color); + } + + // Oops, these contain raw pointers + for(i = 0; i < 4; i++) + { + gbc_sound_channel[i].sample_data = square_pattern_duty[2]; + } + current_debug_state = STEP; + instruction_count = 0; + + reg[CHANGED_PC_STATUS] = 1; + } +} + +u8 savestate_write_buffer[506947]; +u8 *write_mem_ptr; + +void gba_save_state(char *savestate_filename, u16 *screen_capture) +{ + write_mem_ptr = savestate_write_buffer; + file_open(savestate_file, savestate_filename, write); + if(file_check_valid(savestate_file)) + { + time_t current_time; + file_write_mem(savestate_file, screen_capture, 240 * 160 * 2); + + time(¤t_time); + file_write_mem_variable(savestate_file, current_time); + + savestate_block(write_mem); + file_write(savestate_file, savestate_write_buffer, + sizeof(savestate_write_buffer)); + + file_close(savestate_file); + } +} + + +#define memory_savestate_builder(type) \ +void memory_##type##_savestate(file_tag_type savestate_file) \ +{ \ + u32 i; \ + \ + file_##type##_variable(savestate_file, backup_type); \ + file_##type##_variable(savestate_file, sram_size); \ + file_##type##_variable(savestate_file, flash_mode); \ + file_##type##_variable(savestate_file, flash_command_position); \ + file_##type##_variable(savestate_file, flash_bank_ptr); \ + file_##type##_variable(savestate_file, flash_device_id); \ + file_##type##_variable(savestate_file, flash_manufacturer_id); \ + file_##type##_variable(savestate_file, flash_size); \ + file_##type##_variable(savestate_file, eeprom_size); \ + file_##type##_variable(savestate_file, eeprom_mode); \ + file_##type##_variable(savestate_file, eeprom_address_length); \ + file_##type##_variable(savestate_file, eeprom_address); \ + file_##type##_variable(savestate_file, eeprom_counter); \ + file_##type##_variable(savestate_file, rtc_state); \ + file_##type##_variable(savestate_file, rtc_write_mode); \ + file_##type##_array(savestate_file, rtc_registers); \ + file_##type##_variable(savestate_file, rtc_command); \ + file_##type##_array(savestate_file, rtc_data); \ + file_##type##_variable(savestate_file, rtc_status); \ + file_##type##_variable(savestate_file, rtc_data_bytes); \ + file_##type##_variable(savestate_file, rtc_bit_count); \ + file_##type##_array(savestate_file, eeprom_buffer); \ + file_##type##_array(savestate_file, gamepak_filename); \ + file_##type##_array(savestate_file, dma); \ + \ + file_##type(savestate_file, iwram + 0x8000, 0x8000); \ + for(i = 0; i < 8; i++) \ + { \ + file_##type(savestate_file, ewram + (i * 0x10000) + 0x8000, 0x8000); \ + } \ + file_##type(savestate_file, vram, 0x18000); \ + file_##type(savestate_file, oam_ram, 0x400); \ + file_##type(savestate_file, palette_ram, 0x400); \ + file_##type(savestate_file, io_registers, 0x8000); \ + \ + /* This is a hack, for now. */ \ + if((flash_bank_ptr < gamepak_backup) || \ + (flash_bank_ptr > (gamepak_backup + (1024 * 64)))) \ + { \ + flash_bank_ptr = gamepak_backup; \ + } \ +} \ + +memory_savestate_builder(read); +memory_savestate_builder(write_mem); + diff --git a/memory.c b/memory.c deleted file mode 100644 index b93e928..0000000 --- a/memory.c +++ /dev/null @@ -1,3556 +0,0 @@ -/* gameplaySP - * - * Copyright (C) 2006 Exophase - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "common.h" - -/* Sound */ -#define gbc_sound_tone_control_low(channel, address) \ -{ \ - u32 initial_volume = (value >> 12) & 0x0F; \ - u32 envelope_ticks = ((value >> 8) & 0x07) * 4; \ - gbc_sound_channel[channel].length_ticks = 64 - (value & 0x3F); \ - gbc_sound_channel[channel].sample_data = \ - square_pattern_duty[(value >> 6) & 0x03]; \ - gbc_sound_channel[channel].envelope_direction = (value >> 11) & 0x01; \ - gbc_sound_channel[channel].envelope_initial_volume = initial_volume; \ - gbc_sound_channel[channel].envelope_volume = initial_volume; \ - gbc_sound_channel[channel].envelope_initial_ticks = envelope_ticks; \ - gbc_sound_channel[channel].envelope_ticks = envelope_ticks; \ - gbc_sound_channel[channel].envelope_status = (envelope_ticks != 0); \ - gbc_sound_channel[channel].envelope_volume = initial_volume; \ - gbc_sound_update = 1; \ - address16(io_registers, address) = value; \ -} \ - -#define gbc_sound_tone_control_high(channel, address) \ -{ \ - u32 rate = value & 0x7FF; \ - gbc_sound_channel[channel].rate = rate; \ - gbc_sound_channel[channel].frequency_step = \ - float_to_fp16_16(((131072.0 / (2048 - rate)) * 8.0) / sound_frequency); \ - gbc_sound_channel[channel].length_status = (value >> 14) & 0x01; \ - if(value & 0x8000) \ - { \ - gbc_sound_channel[channel].active_flag = 1; \ - gbc_sound_channel[channel].sample_index -= float_to_fp16_16(1.0 / 12.0); \ - gbc_sound_channel[channel].envelope_ticks = \ - gbc_sound_channel[channel].envelope_initial_ticks; \ - gbc_sound_channel[channel].envelope_volume = \ - gbc_sound_channel[channel].envelope_initial_volume; \ - } \ - \ - gbc_sound_update = 1; \ - address16(io_registers, address) = value; \ -} \ - -#define gbc_sound_tone_control_sweep() \ -{ \ - u32 sweep_ticks = ((value >> 4) & 0x07) * 2; \ - gbc_sound_channel[0].sweep_shift = value & 0x07; \ - gbc_sound_channel[0].sweep_direction = (value >> 3) & 0x01; \ - gbc_sound_channel[0].sweep_status = (value != 8); \ - gbc_sound_channel[0].sweep_ticks = sweep_ticks; \ - gbc_sound_channel[0].sweep_initial_ticks = sweep_ticks; \ - gbc_sound_update = 1; \ - address16(io_registers, 0x60) = value; \ -} \ - -#define gbc_sound_wave_control() \ -{ \ - gbc_sound_channel[2].wave_type = (value >> 5) & 0x01; \ - gbc_sound_channel[2].wave_bank = (value >> 6) & 0x01; \ - if(value & 0x80) \ - { \ - gbc_sound_channel[2].master_enable = 1; \ - } \ - else \ - { \ - gbc_sound_channel[2].master_enable = 0; \ - } \ - \ - gbc_sound_update = 1; \ - address16(io_registers, 0x70) = value; \ -} \ - -static u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 }; - -#define gbc_sound_tone_control_low_wave() \ -{ \ - gbc_sound_channel[2].length_ticks = 256 - (value & 0xFF); \ - if((value >> 15) & 0x01) \ - { \ - gbc_sound_channel[2].wave_volume = 12288; \ - } \ - else \ - { \ - gbc_sound_channel[2].wave_volume = \ - gbc_sound_wave_volume[(value >> 13) & 0x03]; \ - } \ - gbc_sound_update = 1; \ - address16(io_registers, 0x72) = value; \ -} \ - -#define gbc_sound_tone_control_high_wave() \ -{ \ - u32 rate = value & 0x7FF; \ - gbc_sound_channel[2].rate = rate; \ - gbc_sound_channel[2].frequency_step = \ - float_to_fp16_16((2097152.0 / (2048 - rate)) / sound_frequency); \ - gbc_sound_channel[2].length_status = (value >> 14) & 0x01; \ - if(value & 0x8000) \ - { \ - gbc_sound_channel[2].sample_index = 0; \ - gbc_sound_channel[2].active_flag = 1; \ - } \ - gbc_sound_update = 1; \ - address16(io_registers, 0x74) = value; \ -} \ - -#define gbc_sound_noise_control() \ -{ \ - u32 dividing_ratio = value & 0x07; \ - u32 frequency_shift = (value >> 4) & 0x0F; \ - if(dividing_ratio == 0) \ - { \ - gbc_sound_channel[3].frequency_step = \ - float_to_fp16_16(1048576.0 / (1 << (frequency_shift + 1)) / \ - sound_frequency); \ - } \ - else \ - { \ - gbc_sound_channel[3].frequency_step = \ - float_to_fp16_16(524288.0 / (dividing_ratio * \ - (1 << (frequency_shift + 1))) / sound_frequency); \ - } \ - gbc_sound_channel[3].noise_type = (value >> 3) & 0x01; \ - gbc_sound_channel[3].length_status = (value >> 14) & 0x01; \ - if(value & 0x8000) \ - { \ - gbc_sound_channel[3].sample_index = 0; \ - gbc_sound_channel[3].active_flag = 1; \ - gbc_sound_channel[3].envelope_ticks = \ - gbc_sound_channel[3].envelope_initial_ticks; \ - gbc_sound_channel[3].envelope_volume = \ - gbc_sound_channel[3].envelope_initial_volume; \ - } \ - gbc_sound_update = 1; \ - address16(io_registers, 0x7C) = value; \ -} \ - -#define gbc_trigger_sound_channel(channel) \ - gbc_sound_master_volume_right = value & 0x07; \ - gbc_sound_master_volume_left = (value >> 4) & 0x07; \ - gbc_sound_channel[channel].status = ((value >> (channel + 8)) & 0x01) | \ - ((value >> (channel + 11)) & 0x03) \ - -#define gbc_trigger_sound() \ -{ \ - gbc_trigger_sound_channel(0); \ - gbc_trigger_sound_channel(1); \ - gbc_trigger_sound_channel(2); \ - gbc_trigger_sound_channel(3); \ - address16(io_registers, 0x80) = value; \ -} \ - -#define trigger_sound() \ -{ \ - timer[0].direct_sound_channels = (((value >> 10) & 0x01) == 0) | \ - ((((value >> 14) & 0x01) == 0) << 1); \ - timer[1].direct_sound_channels = (((value >> 10) & 0x01) == 1) | \ - ((((value >> 14) & 0x01) == 1) << 1); \ - direct_sound_channel[0].volume = (value >> 2) & 0x01; \ - direct_sound_channel[0].status = (value >> 8) & 0x03; \ - direct_sound_channel[1].volume = (value >> 3) & 0x01; \ - direct_sound_channel[1].status = (value >> 12) & 0x03; \ - gbc_sound_master_volume = value & 0x03; \ - \ - if((value >> 11) & 0x01) \ - sound_reset_fifo(0); \ - if((value >> 15) & 0x01) \ - sound_reset_fifo(1); \ - address16(io_registers, 0x82) = value; \ -} \ - -#define sound_on() \ - if(value & 0x80) \ - { \ - if(sound_on != 1) \ - { \ - sound_on = 1; \ - } \ - } \ - else \ - { \ - u32 i; \ - for(i = 0; i < 4; i++) \ - { \ - gbc_sound_channel[i].active_flag = 0; \ - } \ - sound_on = 0; \ - } \ - address16(io_registers, 0x84) = \ - (address16(io_registers, 0x84) & 0x000F) | (value & 0xFFF0); \ - -#define sound_update_frequency_step(timer_number) \ - timer[timer_number].frequency_step = \ - float_to_fp8_24(GBC_BASE_RATE / (timer_reload * sound_frequency)) \ - -/* Main */ -extern timer_type timer[4]; -static u32 prescale_table[] = { 0, 6, 8, 10 }; - -#define count_timer(timer_number) \ - timer[timer_number].reload = 0x10000 - value; \ - if(timer_number < 2) \ - { \ - u32 timer_reload = \ - timer[timer_number].reload << timer[timer_number].prescale; \ - sound_update_frequency_step(timer_number); \ - } \ - -#define adjust_sound_buffer(timer_number, channel) \ - if(timer[timer_number].direct_sound_channels & (0x01 << channel)) \ - { \ - direct_sound_channel[channel].buffer_index = \ - (gbc_sound_buffer_index + buffer_adjust) % BUFFER_SIZE; \ - } \ - -#define trigger_timer(timer_number) \ - if(value & 0x80) \ - { \ - if(timer[timer_number].status == TIMER_INACTIVE) \ - { \ - u32 prescale = prescale_table[value & 0x03]; \ - u32 timer_reload = timer[timer_number].reload; \ - \ - if((value >> 2) & 0x01) \ - timer[timer_number].status = TIMER_CASCADE; \ - else \ - timer[timer_number].status = TIMER_PRESCALE; \ - \ - timer[timer_number].prescale = prescale; \ - timer[timer_number].irq = (value >> 6) & 0x01; \ - \ - address16(io_registers, 0x100 + (timer_number * 4)) = \ - -timer_reload; \ - \ - timer_reload <<= prescale; \ - timer[timer_number].count = timer_reload; \ - \ - if(timer_reload < execute_cycles) \ - execute_cycles = timer_reload; \ - \ - if(timer_number < 2) \ - { \ - u32 buffer_adjust = \ - (u32)(((float)(cpu_ticks - gbc_sound_last_cpu_ticks) * \ - sound_frequency) / GBC_BASE_RATE) * 2; \ - \ - sound_update_frequency_step(timer_number); \ - adjust_sound_buffer(timer_number, 0); \ - adjust_sound_buffer(timer_number, 1); \ - } \ - } \ - } \ - else \ - { \ - if(timer[timer_number].status != TIMER_INACTIVE) \ - { \ - timer[timer_number].status = TIMER_INACTIVE; \ - timer[timer_number].stop_cpu_ticks = cpu_ticks; \ - } \ - } \ - address16(io_registers, 0x102 + (timer_number * 4)) = value; \ - -// This table is configured for sequential access on system defaults - -u32 waitstate_cycles_sequential[16][3] = -{ - { 1, 1, 1 }, // BIOS - { 1, 1, 1 }, // Invalid - { 3, 3, 6 }, // EWRAM (default settings) - { 1, 1, 1 }, // IWRAM - { 1, 1, 1 }, // IO Registers - { 1, 1, 2 }, // Palette RAM - { 1, 1, 2 }, // VRAM - { 1, 1, 2 }, // OAM - { 3, 3, 6 }, // Gamepak (wait 0) - { 3, 3, 6 }, // Gamepak (wait 0) - { 5, 5, 9 }, // Gamepak (wait 1) - { 5, 5, 9 }, // Gamepak (wait 1) - { 9, 9, 17 }, // Gamepak (wait 2) - { 9, 9, 17 }, // Gamepak (wait 2) -}; - -// Different settings for gamepak ws0-2 sequential (2nd) access - -u32 gamepak_waitstate_sequential[2][3][3] = -{ - { - { 3, 3, 6 }, - { 5, 5, 9 }, - { 9, 9, 17 } - }, - { - { 2, 2, 3 }, - { 2, 2, 3 }, - { 2, 2, 3 } - } -}; - -u16 palette_ram[512]; -u16 oam_ram[512]; -u16 palette_ram_converted[512]; -u16 io_registers[1024 * 16]; -u8 ewram[1024 * 256 * 2]; -u8 iwram[1024 * 32 * 2]; -u8 vram[1024 * 96 * 2]; - -u8 bios_rom[1024 * 32]; -u32 bios_read_protect; - -// Up to 128kb, store SRAM, flash ROM, or EEPROM here. -u8 gamepak_backup[1024 * 128]; - -// Keeps us knowing how much we have left. -u8 *gamepak_rom; -u32 gamepak_size; - -dma_transfer_type dma[4]; - -u8 *memory_regions[16]; -u32 memory_limits[16]; - -typedef struct -{ - u32 page_timestamp; - u32 physical_index; -} gamepak_swap_entry_type; - -u32 gamepak_ram_buffer_size; -u32 gamepak_ram_pages; - -// Enough to map the gamepak RAM space. -gamepak_swap_entry_type *gamepak_memory_map; - -// This is global so that it can be kept open for large ROMs to swap -// pages from, so there's no slowdown with opening and closing the file -// a lot. -#ifdef PSP_BUILD - -file_tag_type gamepak_file_large = -1; - -#else - -file_tag_type gamepak_file_large = NULL; - -#endif - -u32 direct_map_vram = 0; - -// Writes to these respective locations should trigger an update -// so the related subsystem may react to it. - -// If OAM is written to: -u32 oam_update = 1; - -// If GBC audio is written to: -u32 gbc_sound_update = 0; - -// If the GBC audio waveform is modified: -u32 gbc_sound_wave_update = 0; - -// If the backup space is written (only update once this hits 0) -u32 backup_update = 0; - -// Write out backup file this many cycles after the most recent -// backup write. -const u32 write_backup_delay = 10; - - -typedef enum -{ - BACKUP_SRAM, - BACKUP_FLASH, - BACKUP_EEPROM, - BACKUP_NONE -} backup_type_type; - -typedef enum -{ - SRAM_SIZE_32KB, - SRAM_SIZE_64KB -} sram_size_type; - -// Keep it 32KB until the upper 64KB is accessed, then make it 64KB. - -backup_type_type backup_type = BACKUP_NONE; -sram_size_type sram_size = SRAM_SIZE_32KB; - -typedef enum -{ - FLASH_BASE_MODE, - FLASH_ERASE_MODE, - FLASH_ID_MODE, - FLASH_WRITE_MODE, - FLASH_BANKSWITCH_MODE -} flash_mode_type; - -typedef enum -{ - FLASH_SIZE_64KB, - FLASH_SIZE_128KB -} flash_size_type; - -flash_mode_type flash_mode = FLASH_BASE_MODE; -u32 flash_command_position = 0; -u8 *flash_bank_ptr = gamepak_backup; - -flash_device_id_type flash_device_id = FLASH_DEVICE_MACRONIX_64KB; -flash_manufacturer_id_type flash_manufacturer_id = - FLASH_MANUFACTURER_MACRONIX; -flash_size_type flash_size = FLASH_SIZE_64KB; - -u8 read_backup(u32 address) -{ - u8 value = 0; - - if(backup_type == BACKUP_NONE) - backup_type = BACKUP_SRAM; - - if(backup_type == BACKUP_SRAM) - { - value = gamepak_backup[address]; - } - else - - if(flash_mode == FLASH_ID_MODE) - { - /* ID manufacturer type */ - if(address == 0x0000) - value = flash_manufacturer_id; - else - - /* ID device type */ - if(address == 0x0001) - value = flash_device_id; - } - else - { - value = flash_bank_ptr[address]; - } - - return value; -} - -#define read_backup8() \ - value = read_backup(address & 0xFFFF) \ - -#define read_backup16() \ - value = 0 \ - -#define read_backup32() \ - value = 0 \ - - -// EEPROM is 512 bytes by default; it is autodetecte as 8KB if -// 14bit address DMAs are made (this is done in the DMA handler). - -typedef enum -{ - EEPROM_512_BYTE, - EEPROM_8_KBYTE -} eeprom_size_type; - -typedef enum -{ - EEPROM_BASE_MODE, - EEPROM_READ_MODE, - EEPROM_READ_HEADER_MODE, - EEPROM_ADDRESS_MODE, - EEPROM_WRITE_MODE, - EEPROM_WRITE_ADDRESS_MODE, - EEPROM_ADDRESS_FOOTER_MODE, - EEPROM_WRITE_FOOTER_MODE -} eeprom_mode_type; - - -eeprom_size_type eeprom_size = EEPROM_512_BYTE; -eeprom_mode_type eeprom_mode = EEPROM_BASE_MODE; -u32 eeprom_address_length; -u32 eeprom_address = 0; -s32 eeprom_counter = 0; -u8 eeprom_buffer[8]; - - -void function_cc write_eeprom(u32 address, u32 value) -{ - switch(eeprom_mode) - { - case EEPROM_BASE_MODE: - backup_type = BACKUP_EEPROM; - eeprom_buffer[0] |= (value & 0x01) << (1 - eeprom_counter); - eeprom_counter++; - if(eeprom_counter == 2) - { - if(eeprom_size == EEPROM_512_BYTE) - eeprom_address_length = 6; - else - eeprom_address_length = 14; - - eeprom_counter = 0; - - switch(eeprom_buffer[0] & 0x03) - { - case 0x02: - eeprom_mode = EEPROM_WRITE_ADDRESS_MODE; - break; - - case 0x03: - eeprom_mode = EEPROM_ADDRESS_MODE; - break; - } - address16(eeprom_buffer, 0) = 0; - } - break; - - case EEPROM_ADDRESS_MODE: - case EEPROM_WRITE_ADDRESS_MODE: - eeprom_buffer[eeprom_counter / 8] - |= (value & 0x01) << (7 - (eeprom_counter % 8)); - eeprom_counter++; - if(eeprom_counter == eeprom_address_length) - { - if(eeprom_size == EEPROM_512_BYTE) - { - eeprom_address = - (address16(eeprom_buffer, 0) >> 2) * 8; - } - else - { - eeprom_address = (((u32)eeprom_buffer[1] >> 2) | - ((u32)eeprom_buffer[0] << 6)) * 8; - } - - address16(eeprom_buffer, 0) = 0; - eeprom_counter = 0; - - if(eeprom_mode == EEPROM_ADDRESS_MODE) - { - eeprom_mode = EEPROM_ADDRESS_FOOTER_MODE; - } - else - { - eeprom_mode = EEPROM_WRITE_MODE; - memset(gamepak_backup + eeprom_address, 0, 8); - } - } - break; - - case EEPROM_WRITE_MODE: - gamepak_backup[eeprom_address + (eeprom_counter / 8)] |= - (value & 0x01) << (7 - (eeprom_counter % 8)); - eeprom_counter++; - if(eeprom_counter == 64) - { - backup_update = write_backup_delay; - eeprom_counter = 0; - eeprom_mode = EEPROM_WRITE_FOOTER_MODE; - } - break; - - case EEPROM_ADDRESS_FOOTER_MODE: - case EEPROM_WRITE_FOOTER_MODE: - eeprom_counter = 0; - if(eeprom_mode == EEPROM_ADDRESS_FOOTER_MODE) - { - eeprom_mode = EEPROM_READ_HEADER_MODE; - } - else - { - eeprom_mode = EEPROM_BASE_MODE; - } - break; - - default: - break; - } -} - -#define read_memory_gamepak(type) \ - u32 gamepak_index = address >> 15; \ - u8 *map = memory_map_read[gamepak_index]; \ - \ - if(map == NULL) \ - map = load_gamepak_page(gamepak_index & 0x3FF); \ - \ - value = address##type(map, address & 0x7FFF) \ - -#define read_open8() \ - if(!(reg[REG_CPSR] & 0x20)) \ - value = read_memory8(reg[REG_PC] + 4 + (address & 0x03)); \ - else \ - value = read_memory8(reg[REG_PC] + 2 + (address & 0x01)) \ - -#define read_open16() \ - if(!(reg[REG_CPSR] & 0x20)) \ - value = read_memory16(reg[REG_PC] + 4 + (address & 0x02)); \ - else \ - value = read_memory16(reg[REG_PC] + 2) \ - -#define read_open32() \ - if(!(reg[REG_CPSR] & 0x20)) \ - { \ - value = read_memory32(reg[REG_PC] + 4); \ - } \ - else \ - { \ - u32 current_instruction = read_memory16(reg[REG_PC] + 2); \ - value = current_instruction | (current_instruction << 16); \ - } \ - -u32 function_cc read_eeprom() -{ - u32 value; - - switch(eeprom_mode) - { - case EEPROM_BASE_MODE: - value = 1; - break; - - case EEPROM_READ_MODE: - value = (gamepak_backup[eeprom_address + (eeprom_counter / 8)] >> - (7 - (eeprom_counter % 8))) & 0x01; - eeprom_counter++; - if(eeprom_counter == 64) - { - eeprom_counter = 0; - eeprom_mode = EEPROM_BASE_MODE; - } - break; - - case EEPROM_READ_HEADER_MODE: - value = 0; - eeprom_counter++; - if(eeprom_counter == 4) - { - eeprom_mode = EEPROM_READ_MODE; - eeprom_counter = 0; - } - break; - - default: - value = 0; - break; - } - - return value; -} - - -#define read_memory(type) \ - switch(address >> 24) \ - { \ - case 0x00: \ - /* BIOS */ \ - if(reg[REG_PC] >= 0x4000) \ - value = address##type(&bios_read_protect, address & 0x03); \ - else \ - value = address##type(bios_rom, address & 0x3FFF); \ - break; \ - \ - case 0x02: \ - /* external work RAM */ \ - address = (address & 0x7FFF) + ((address & 0x38000) * 2) + 0x8000; \ - value = address##type(ewram, address); \ - break; \ - \ - case 0x03: \ - /* internal work RAM */ \ - value = address##type(iwram, (address & 0x7FFF) + 0x8000); \ - break; \ - \ - case 0x04: \ - /* I/O registers */ \ - value = address##type(io_registers, address & 0x3FF); \ - break; \ - \ - case 0x05: \ - /* palette RAM */ \ - value = address##type(palette_ram, address & 0x3FF); \ - break; \ - \ - case 0x06: \ - /* VRAM */ \ - address &= 0x1FFFF; \ - if(address > 0x18000) \ - address -= 0x8000; \ - \ - value = address##type(vram, address); \ - break; \ - \ - case 0x07: \ - /* OAM RAM */ \ - value = address##type(oam_ram, address & 0x3FF); \ - break; \ - \ - case 0x08: \ - case 0x09: \ - case 0x0A: \ - case 0x0B: \ - case 0x0C: \ - /* gamepak ROM */ \ - if((address & 0x1FFFFFF) >= gamepak_size) \ - { \ - value = 0; \ - } \ - else \ - { \ - read_memory_gamepak(type); \ - } \ - break; \ - \ - case 0x0D: \ - if((address & 0x1FFFFFF) < gamepak_size) \ - { \ - read_memory_gamepak(type); \ - } \ - else \ - { \ - value = read_eeprom(); \ - } \ - \ - break; \ - \ - case 0x0E: \ - case 0x0F: \ - read_backup##type(); \ - break; \ - \ - default: \ - read_open##type(); \ - break; \ - } \ - -#define trigger_dma(dma_number) \ - if(value & 0x8000) \ - { \ - if(dma[dma_number].start_type == DMA_INACTIVE) \ - { \ - u32 start_type = (value >> 12) & 0x03; \ - u32 dest_address = address32(io_registers, (dma_number * 12) + 0xB4) & \ - 0xFFFFFFF; \ - \ - dma[dma_number].dma_channel = dma_number; \ - dma[dma_number].source_address = \ - address32(io_registers, (dma_number * 12) + 0xB0) & 0xFFFFFFF; \ - dma[dma_number].dest_address = dest_address; \ - dma[dma_number].source_direction = (value >> 7) & 0x03; \ - dma[dma_number].repeat_type = (value >> 9) & 0x01; \ - dma[dma_number].start_type = start_type; \ - dma[dma_number].irq = (value >> 14) & 0x01; \ - \ - /* If it is sound FIFO DMA make sure the settings are a certain way */ \ - if((dma_number >= 1) && (dma_number <= 2) && \ - (start_type == DMA_START_SPECIAL)) \ - { \ - dma[dma_number].length_type = DMA_32BIT; \ - dma[dma_number].length = 4; \ - dma[dma_number].dest_direction = DMA_FIXED; \ - if(dest_address == 0x40000A4) \ - dma[dma_number].direct_sound_channel = DMA_DIRECT_SOUND_B; \ - else \ - dma[dma_number].direct_sound_channel = DMA_DIRECT_SOUND_A; \ - } \ - else \ - { \ - u32 length = \ - address16(io_registers, (dma_number * 12) + 0xB8); \ - \ - if((dma_number == 3) && ((dest_address >> 24) == 0x0D) && \ - ((length & 0x1F) == 17)) \ - { \ - eeprom_size = EEPROM_8_KBYTE; \ - } \ - \ - if(dma_number < 3) \ - length &= 0x3FFF; \ - \ - if(length == 0) \ - { \ - if(dma_number == 3) \ - length = 0x10000; \ - else \ - length = 0x04000; \ - } \ - \ - dma[dma_number].length = length; \ - dma[dma_number].length_type = (value >> 10) & 0x01; \ - dma[dma_number].dest_direction = (value >> 5) & 0x03; \ - } \ - \ - address16(io_registers, (dma_number * 12) + 0xBA) = value; \ - if(start_type == DMA_START_IMMEDIATELY) \ - return dma_transfer(dma + dma_number); \ - } \ - } \ - else \ - { \ - dma[dma_number].start_type = DMA_INACTIVE; \ - dma[dma_number].direct_sound_channel = DMA_NO_DIRECT_SOUND; \ - address16(io_registers, (dma_number * 12) + 0xBA) = value; \ - } \ - - -#define access_register8_high(address) \ - value = (value << 8) | (address8(io_registers, address)) \ - -#define access_register8_low(address) \ - value = ((address8(io_registers, address + 1)) << 8) | value \ - -#define access_register16_high(address) \ - value = (value << 16) | (address16(io_registers, address)) \ - -#define access_register16_low(address) \ - value = ((address16(io_registers, address + 2)) << 16) | value \ - -cpu_alert_type function_cc write_io_register8(u32 address, u32 value) -{ - switch(address) - { - case 0x00: - { - u32 dispcnt = io_registers[REG_DISPCNT]; - - if((value & 0x07) != (dispcnt & 0x07)) - oam_update = 1; - - address8(io_registers, 0x00) = value; - break; - } - - // DISPSTAT (lower byte) - case 0x04: - address8(io_registers, 0x04) = - (address8(io_registers, 0x04) & 0x07) | (value & ~0x07); - break; - - // VCOUNT - case 0x06: - case 0x07: - break; - - // BG2 reference X - case 0x28: - access_register8_low(0x28); - access_register16_low(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - case 0x29: - access_register8_high(0x28); - access_register16_low(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - case 0x2A: - access_register8_low(0x2A); - access_register16_high(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - case 0x2B: - access_register8_high(0x2A); - access_register16_high(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - // BG2 reference Y - case 0x2C: - access_register8_low(0x2C); - access_register16_low(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - case 0x2D: - access_register8_high(0x2C); - access_register16_low(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - case 0x2E: - access_register8_low(0x2E); - access_register16_high(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - case 0x2F: - access_register8_high(0x2E); - access_register16_high(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - // BG3 reference X - case 0x38: - access_register8_low(0x38); - access_register16_low(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - case 0x39: - access_register8_high(0x38); - access_register16_low(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - case 0x3A: - access_register8_low(0x3A); - access_register16_high(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - case 0x3B: - access_register8_high(0x3A); - access_register16_high(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - // BG3 reference Y - case 0x3C: - access_register8_low(0x3C); - access_register16_low(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - case 0x3D: - access_register8_high(0x3C); - access_register16_low(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - case 0x3E: - access_register8_low(0x3E); - access_register16_high(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - case 0x3F: - access_register8_high(0x3E); - access_register16_high(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - // Sound 1 control sweep - case 0x60: - access_register8_low(0x60); - gbc_sound_tone_control_sweep(); - break; - - case 0x61: - access_register8_low(0x60); - gbc_sound_tone_control_sweep(); - break; - - // Sound 1 control duty/length/envelope - case 0x62: - access_register8_low(0x62); - gbc_sound_tone_control_low(0, 0x62); - break; - - case 0x63: - access_register8_high(0x62); - gbc_sound_tone_control_low(0, 0x62); - break; - - // Sound 1 control frequency - case 0x64: - access_register8_low(0x64); - gbc_sound_tone_control_high(0, 0x64); - break; - - case 0x65: - access_register8_high(0x64); - gbc_sound_tone_control_high(0, 0x64); - break; - - // Sound 2 control duty/length/envelope - case 0x68: - access_register8_low(0x68); - gbc_sound_tone_control_low(1, 0x68); - break; - - case 0x69: - access_register8_high(0x68); - gbc_sound_tone_control_low(1, 0x68); - break; - - // Sound 2 control frequency - case 0x6C: - access_register8_low(0x6C); - gbc_sound_tone_control_high(1, 0x6C); - break; - - case 0x6D: - access_register8_high(0x6C); - gbc_sound_tone_control_high(1, 0x6C); - break; - - // Sound 3 control wave - case 0x70: - access_register8_low(0x70); - gbc_sound_wave_control(); - break; - - case 0x71: - access_register8_high(0x70); - gbc_sound_wave_control(); - break; - - // Sound 3 control length/volume - case 0x72: - access_register8_low(0x72); - gbc_sound_tone_control_low_wave(); - break; - - case 0x73: - access_register8_high(0x72); - gbc_sound_tone_control_low_wave(); - break; - - // Sound 3 control frequency - case 0x74: - access_register8_low(0x74); - gbc_sound_tone_control_high_wave(); - break; - - case 0x75: - access_register8_high(0x74); - gbc_sound_tone_control_high_wave(); - break; - - // Sound 4 control length/envelope - case 0x78: - access_register8_low(0x78); - gbc_sound_tone_control_low(3, 0x78); - break; - - case 0x79: - access_register8_high(0x78); - gbc_sound_tone_control_low(3, 0x78); - break; - - // Sound 4 control frequency - case 0x7C: - access_register8_low(0x7C); - gbc_sound_noise_control(); - break; - - case 0x7D: - access_register8_high(0x7C); - gbc_sound_noise_control(); - break; - - // Sound control L - case 0x80: - access_register8_low(0x80); - gbc_trigger_sound(); - break; - - case 0x81: - access_register8_high(0x80); - gbc_trigger_sound(); - break; - - // Sound control H - case 0x82: - access_register8_low(0x82); - trigger_sound(); - break; - - case 0x83: - access_register8_high(0x82); - trigger_sound(); - break; - - // Sound control X - case 0x84: - sound_on(); - break; - - // Sound wave RAM - case 0x90 ... 0x9F: - gbc_sound_wave_update = 1; - address8(io_registers, address) = value; - break; - - // Sound FIFO A - case 0xA0: - sound_timer_queue8(0, value); - break; - - // Sound FIFO B - case 0xA4: - sound_timer_queue8(1, value); - break; - - // DMA control (trigger byte) - case 0xBB: - access_register8_low(0xBA); - trigger_dma(0); - break; - - case 0xC7: - access_register8_low(0xC6); - trigger_dma(1); - break; - - case 0xD3: - access_register8_low(0xD2); - trigger_dma(2); - break; - - case 0xDF: - access_register8_low(0xDE); - trigger_dma(3); - break; - - // Timer counts - case 0x100: - access_register8_low(0x100); - count_timer(0); - break; - - case 0x101: - access_register8_high(0x100); - count_timer(0); - break; - - case 0x104: - access_register8_low(0x104); - count_timer(1); - break; - - case 0x105: - access_register8_high(0x104); - count_timer(1); - break; - - case 0x108: - access_register8_low(0x108); - count_timer(2); - break; - - case 0x109: - access_register8_high(0x108); - count_timer(2); - break; - - case 0x10C: - access_register8_low(0x10C); - count_timer(3); - break; - - case 0x10D: - access_register8_high(0x10C); - count_timer(3); - break; - - // Timer control (trigger byte) - case 0x103: - access_register8_low(0x102); - trigger_timer(0); - break; - - case 0x107: - access_register8_low(0x106); - trigger_timer(1); - break; - - case 0x10B: - access_register8_low(0x10A); - trigger_timer(2); - break; - - case 0x10F: - access_register8_low(0x10E); - trigger_timer(3); - break; - - // IF - case 0x202: - address8(io_registers, 0x202) &= ~value; - break; - - case 0x203: - address8(io_registers, 0x203) &= ~value; - break; - - // Halt - case 0x301: - if((value & 0x01) == 0) - reg[CPU_HALT_STATE] = CPU_HALT; - else - reg[CPU_HALT_STATE] = CPU_STOP; - - return CPU_ALERT_HALT; - break; - - default: - address8(io_registers, address) = value; - break; - } - - return CPU_ALERT_NONE; -} - -cpu_alert_type function_cc write_io_register16(u32 address, u32 value) -{ - switch(address) - { - case 0x00: - { - u32 dispcnt = io_registers[REG_DISPCNT]; - if((value & 0x07) != (dispcnt & 0x07)) - oam_update = 1; - - address16(io_registers, 0x00) = value; - break; - } - - // DISPSTAT - case 0x04: - address16(io_registers, 0x04) = - (address16(io_registers, 0x04) & 0x07) | (value & ~0x07); - break; - - // VCOUNT - case 0x06: - break; - - // BG2 reference X - case 0x28: - access_register16_low(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - case 0x2A: - access_register16_high(0x28); - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - // BG2 reference Y - case 0x2C: - access_register16_low(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - case 0x2E: - access_register16_high(0x2C); - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - // BG3 reference X - - case 0x38: - access_register16_low(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - case 0x3A: - access_register16_high(0x38); - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - // BG3 reference Y - case 0x3C: - access_register16_low(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - case 0x3E: - access_register16_high(0x3C); - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - // Sound 1 control sweep - case 0x60: - gbc_sound_tone_control_sweep(); - break; - - // Sound 1 control duty/length/envelope - case 0x62: - gbc_sound_tone_control_low(0, 0x62); - break; - - // Sound 1 control frequency - case 0x64: - gbc_sound_tone_control_high(0, 0x64); - break; - - // Sound 2 control duty/length/envelope - case 0x68: - gbc_sound_tone_control_low(1, 0x68); - break; - - // Sound 2 control frequency - case 0x6C: - gbc_sound_tone_control_high(1, 0x6C); - break; - - // Sound 3 control wave - case 0x70: - gbc_sound_wave_control(); - break; - - // Sound 3 control length/volume - case 0x72: - gbc_sound_tone_control_low_wave(); - break; - - // Sound 3 control frequency - case 0x74: - gbc_sound_tone_control_high_wave(); - break; - - // Sound 4 control length/envelope - case 0x78: - gbc_sound_tone_control_low(3, 0x78); - break; - - // Sound 4 control frequency - case 0x7C: - gbc_sound_noise_control(); - break; - - // Sound control L - case 0x80: - gbc_trigger_sound(); - break; - - // Sound control H - case 0x82: - trigger_sound(); - break; - - // Sound control X - case 0x84: - sound_on(); - break; - - // Sound wave RAM - case 0x90 ... 0x9E: - gbc_sound_wave_update = 1; - address16(io_registers, address) = value; - break; - - // Sound FIFO A - case 0xA0: - sound_timer_queue16(0, value); - break; - - // Sound FIFO B - case 0xA4: - sound_timer_queue16(1, value); - break; - - // DMA control - case 0xBA: - trigger_dma(0); - break; - - case 0xC6: - trigger_dma(1); - break; - - case 0xD2: - trigger_dma(2); - break; - - case 0xDE: - trigger_dma(3); - break; - - // Timer counts - case 0x100: - count_timer(0); - break; - - case 0x104: - count_timer(1); - break; - - case 0x108: - count_timer(2); - break; - - case 0x10C: - count_timer(3); - break; - - // Timer control - case 0x102: - trigger_timer(0); - break; - - case 0x106: - trigger_timer(1); - break; - - case 0x10A: - trigger_timer(2); - break; - - case 0x10E: - trigger_timer(3); - break; - - // P1 - case 0x130: - break; - - // Interrupt flag - case 0x202: - address16(io_registers, 0x202) &= ~value; - break; - - // WAITCNT - case 0x204: - break; - - // Halt - case 0x300: - if(((value >> 8) & 0x01) == 0) - reg[CPU_HALT_STATE] = CPU_HALT; - else - reg[CPU_HALT_STATE] = CPU_STOP; - - return CPU_ALERT_HALT; - - default: - address16(io_registers, address) = value; - break; - } - - return CPU_ALERT_NONE; -} - - -cpu_alert_type function_cc write_io_register32(u32 address, u32 value) -{ - switch(address) - { - // BG2 reference X - case 0x28: - affine_reference_x[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x28) = value; - break; - - // BG2 reference Y - case 0x2C: - affine_reference_y[0] = (s32)(value << 4) >> 4; - address32(io_registers, 0x2C) = value; - break; - - // BG3 reference X - case 0x38: - affine_reference_x[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x38) = value; - break; - - // BG3 reference Y - case 0x3C: - affine_reference_y[1] = (s32)(value << 4) >> 4; - address32(io_registers, 0x3C) = value; - break; - - // Sound FIFO A - case 0xA0: - sound_timer_queue32(0, value); - break; - - // Sound FIFO B - case 0xA4: - sound_timer_queue32(1, value); - break; - - default: - { - cpu_alert_type alert_low = - write_io_register16(address, value & 0xFFFF); - - cpu_alert_type alert_high = - write_io_register16(address + 2, value >> 16); - - if(alert_high) - return alert_high; - - return alert_low; - } - } - - return CPU_ALERT_NONE; -} - -#define write_palette8(address, value) \ - -#define write_palette16(address, value) \ -{ \ - u32 palette_address = address; \ - address16(palette_ram, palette_address) = value; \ - convert_palette(value); \ - address16(palette_ram_converted, palette_address) = value; \ -} \ - -#define write_palette32(address, value) \ -{ \ - u32 palette_address = address; \ - u32 value_high = value >> 16; \ - u32 value_low = value & 0xFFFF; \ - address32(palette_ram, palette_address) = value; \ - convert_palette(value_high); \ - convert_palette(value_low); \ - value = (value_high << 16) | value_low; \ - address32(palette_ram_converted, palette_address) = value; \ -} \ - - -void function_cc write_backup(u32 address, u32 value) -{ - value &= 0xFF; - - if(backup_type == BACKUP_NONE) - backup_type = BACKUP_SRAM; - - - // gamepak SRAM or Flash ROM - if((address == 0x5555) && (flash_mode != FLASH_WRITE_MODE)) - { - if((flash_command_position == 0) && (value == 0xAA)) - { - backup_type = BACKUP_FLASH; - flash_command_position = 1; - } - - if(flash_command_position == 2) - { - switch(value) - { - case 0x90: - // Enter ID mode, this also tells the emulator that we're using - // flash, not SRAM - - if(flash_mode == FLASH_BASE_MODE) - flash_mode = FLASH_ID_MODE; - - break; - - case 0x80: - // Enter erase mode - if(flash_mode == FLASH_BASE_MODE) - flash_mode = FLASH_ERASE_MODE; - break; - - case 0xF0: - // Terminate ID mode - if(flash_mode == FLASH_ID_MODE) - flash_mode = FLASH_BASE_MODE; - break; - - case 0xA0: - // Write mode - if(flash_mode == FLASH_BASE_MODE) - flash_mode = FLASH_WRITE_MODE; - break; - - case 0xB0: - // Bank switch - // Here the chip is now officially 128KB. - flash_size = FLASH_SIZE_128KB; - if(flash_mode == FLASH_BASE_MODE) - flash_mode = FLASH_BANKSWITCH_MODE; - break; - - case 0x10: - // Erase chip - if(flash_mode == FLASH_ERASE_MODE) - { - if(flash_size == FLASH_SIZE_64KB) - memset(gamepak_backup, 0xFF, 1024 * 64); - else - memset(gamepak_backup, 0xFF, 1024 * 128); - backup_update = write_backup_delay; - flash_mode = FLASH_BASE_MODE; - } - break; - - default: - break; - } - flash_command_position = 0; - } - if(backup_type == BACKUP_SRAM) - gamepak_backup[0x5555] = value; - } - else - - if((address == 0x2AAA) && (value == 0x55) && - (flash_command_position == 1)) - { - flash_command_position = 2; - } - else - { - if((flash_command_position == 2) && - (flash_mode == FLASH_ERASE_MODE) && (value == 0x30)) - { - // Erase sector - memset(flash_bank_ptr + (address & 0xF000), 0xFF, 1024 * 4); - backup_update = write_backup_delay; - flash_mode = FLASH_BASE_MODE; - flash_command_position = 0; - } - else - - if((flash_command_position == 0) && - (flash_mode == FLASH_BANKSWITCH_MODE) && (address == 0x0000) && - (flash_size == FLASH_SIZE_128KB)) - { - flash_bank_ptr = gamepak_backup + ((value & 0x01) * (1024 * 64)); - flash_mode = FLASH_BASE_MODE; - } - else - - if((flash_command_position == 0) && (flash_mode == FLASH_WRITE_MODE)) - { - // Write value to flash ROM - backup_update = write_backup_delay; - flash_bank_ptr[address] = value; - flash_mode = FLASH_BASE_MODE; - } - else - - if(backup_type == BACKUP_SRAM) - { - // Write value to SRAM - backup_update = write_backup_delay; - // Hit 64KB territory? - if(address >= 0x8000) - sram_size = SRAM_SIZE_64KB; - gamepak_backup[address] = value; - } - } -} - -#define write_backup8() \ - write_backup(address & 0xFFFF, value) \ - -#define write_backup16() \ - -#define write_backup32() \ - -#define write_vram8() \ - address &= ~0x01; \ - address16(vram, address) = ((value << 8) | value) \ - -#define write_vram16() \ - address16(vram, address) = value \ - -#define write_vram32() \ - address32(vram, address) = value \ - -// RTC code derived from VBA's (due to lack of any real publically available -// documentation...) - -typedef enum -{ - RTC_DISABLED, - RTC_IDLE, - RTC_COMMAND, - RTC_OUTPUT_DATA, - RTC_INPUT_DATA -} rtc_state_type; - -typedef enum -{ - RTC_COMMAND_RESET = 0x60, - RTC_COMMAND_WRITE_STATUS = 0x62, - RTC_COMMAND_READ_STATUS = 0x63, - RTC_COMMAND_OUTPUT_TIME_FULL = 0x65, - RTC_COMMAND_OUTPUT_TIME = 0x67 -} rtc_command_type; - -typedef enum -{ - RTC_WRITE_TIME, - RTC_WRITE_TIME_FULL, - RTC_WRITE_STATUS -} rtc_write_mode_type; - -rtc_state_type rtc_state = RTC_DISABLED; -rtc_write_mode_type rtc_write_mode; -u8 rtc_registers[3]; -u32 rtc_command; -u32 rtc_data[12]; -u32 rtc_status = 0x40; -u32 rtc_data_bytes; -s32 rtc_bit_count; - -u32 encode_bcd(u8 value) -{ - return ((value / 10) << 4) | (value % 10); -} - -#define write_rtc_register(index, _value) \ - update_address = 0x80000C4 + (index * 2); \ - rtc_registers[index] = _value; \ - rtc_page_index = update_address >> 15; \ - map = memory_map_read[rtc_page_index]; \ - \ - if(map == NULL) \ - map = load_gamepak_page(rtc_page_index & 0x3FF); \ - \ - address16(map, update_address & 0x7FFF) = _value \ - -void function_cc write_rtc(u32 address, u32 value) -{ - u32 rtc_page_index; - u32 update_address; - u8 *map; - - value &= 0xFFFF; - - switch(address) - { - // RTC command - // Bit 0: SCHK, perform action - // Bit 1: IO, input/output command data - // Bit 2: CS, select input/output? If high make I/O write only - case 0xC4: - if(rtc_state == RTC_DISABLED) - rtc_state = RTC_IDLE; - if(!(rtc_registers[0] & 0x04)) - value = (rtc_registers[0] & 0x02) | (value & ~0x02); - if(rtc_registers[2] & 0x01) - { - // To begin writing a command 1, 5 must be written to the command - // registers. - if((rtc_state == RTC_IDLE) && (rtc_registers[0] == 0x01) && - (value == 0x05)) - { - // We're now ready to begin receiving a command. - write_rtc_register(0, value); - rtc_state = RTC_COMMAND; - rtc_command = 0; - rtc_bit_count = 7; - } - else - { - write_rtc_register(0, value); - switch(rtc_state) - { - // Accumulate RTC command by receiving the next bit, and if we - // have accumulated enough bits to form a complete command - // execute it. - case RTC_COMMAND: - if(rtc_registers[0] & 0x01) - { - rtc_command |= ((value & 0x02) >> 1) << rtc_bit_count; - rtc_bit_count--; - } - - // Have we received a full RTC command? If so execute it. - if(rtc_bit_count < 0) - { - switch(rtc_command) - { - // Resets RTC - case RTC_COMMAND_RESET: - rtc_state = RTC_IDLE; - memset(rtc_registers, 0, sizeof(rtc_registers)); - break; - - // Sets status of RTC - case RTC_COMMAND_WRITE_STATUS: - rtc_state = RTC_INPUT_DATA; - rtc_data_bytes = 1; - rtc_write_mode = RTC_WRITE_STATUS; - break; - - // Outputs current status of RTC - case RTC_COMMAND_READ_STATUS: - rtc_state = RTC_OUTPUT_DATA; - rtc_data_bytes = 1; - rtc_data[0] = rtc_status; - break; - - // Actually outputs the time, all of it - case RTC_COMMAND_OUTPUT_TIME_FULL: - { - struct tm *current_time; - time_t current_time_flat; - u32 day_of_week; - - time(¤t_time_flat); - current_time = localtime(¤t_time_flat); - - day_of_week = current_time->tm_wday; - if(day_of_week == 0) - day_of_week = 6; - else - day_of_week--; - - rtc_state = RTC_OUTPUT_DATA; - rtc_data_bytes = 7; - rtc_data[0] = encode_bcd(current_time->tm_year % 100); - rtc_data[1] = encode_bcd(current_time->tm_mon + 1); - rtc_data[2] = encode_bcd(current_time->tm_mday); - rtc_data[3] = encode_bcd(day_of_week); - rtc_data[4] = encode_bcd(current_time->tm_hour); - rtc_data[5] = encode_bcd(current_time->tm_min); - rtc_data[6] = encode_bcd(current_time->tm_sec); - - break; - } - - // Only outputs the current time of day. - case RTC_COMMAND_OUTPUT_TIME: - { - struct tm *current_time; - time_t current_time_flat; - - time(¤t_time_flat); - current_time = localtime(¤t_time_flat); - - rtc_state = RTC_OUTPUT_DATA; - rtc_data_bytes = 3; - rtc_data[0] = encode_bcd(current_time->tm_hour); - rtc_data[1] = encode_bcd(current_time->tm_min); - rtc_data[2] = encode_bcd(current_time->tm_sec); - break; - } - } - rtc_bit_count = 0; - } - break; - - // Receive parameters from the game as input to the RTC - // for a given command. Read one bit at a time. - case RTC_INPUT_DATA: - // Bit 1 of parameter A must be high for input - if(rtc_registers[1] & 0x02) - { - // Read next bit for input - if(!(value & 0x01)) - { - rtc_data[rtc_bit_count >> 3] |= - ((value & 0x01) << (7 - (rtc_bit_count & 0x07))); - } - else - { - rtc_bit_count++; - - if(rtc_bit_count == (rtc_data_bytes * 8)) - { - rtc_state = RTC_IDLE; - switch(rtc_write_mode) - { - case RTC_WRITE_STATUS: - rtc_status = rtc_data[0]; - break; - - default: - break; - } - } - } - } - break; - - case RTC_OUTPUT_DATA: - // Bit 1 of parameter A must be low for output - if(!(rtc_registers[1] & 0x02)) - { - // Write next bit to output, on bit 1 of parameter B - if(!(value & 0x01)) - { - u8 current_output_byte = rtc_registers[2]; - - current_output_byte = - (current_output_byte & ~0x02) | - (((rtc_data[rtc_bit_count >> 3] >> - (rtc_bit_count & 0x07)) & 0x01) << 1); - - write_rtc_register(0, current_output_byte); - - } - else - { - rtc_bit_count++; - - if(rtc_bit_count == (rtc_data_bytes * 8)) - { - rtc_state = RTC_IDLE; - memset(rtc_registers, 0, sizeof(rtc_registers)); - } - } - } - break; - - default: - break; - } - } - } - else - { - write_rtc_register(2, value); - } - break; - - // Write parameter A - case 0xC6: - write_rtc_register(1, value); - break; - - // Write parameter B - case 0xC8: - write_rtc_register(2, value); - break; - } -} - -#define write_rtc8() \ - -#define write_rtc16() \ - write_rtc(address & 0xFF, value) \ - -#define write_rtc32() \ - -#define write_memory(type) \ - switch(address >> 24) \ - { \ - case 0x02: \ - /* external work RAM */ \ - address = (address & 0x7FFF) + ((address & 0x38000) * 2) + 0x8000; \ - address##type(ewram, address) = value; \ - break; \ - \ - case 0x03: \ - /* internal work RAM */ \ - address##type(iwram, (address & 0x7FFF) + 0x8000) = value; \ - break; \ - \ - case 0x04: \ - /* I/O registers */ \ - return write_io_register##type(address & 0x3FF, value); \ - \ - case 0x05: \ - /* palette RAM */ \ - write_palette##type(address & 0x3FF, value); \ - break; \ - \ - case 0x06: \ - /* VRAM */ \ - address &= 0x1FFFF; \ - if(address >= 0x18000) \ - address -= 0x8000; \ - \ - write_vram##type(); \ - break; \ - \ - case 0x07: \ - /* OAM RAM */ \ - oam_update = 1; \ - address##type(oam_ram, address & 0x3FF) = value; \ - break; \ - \ - case 0x08: \ - /* gamepak ROM or RTC */ \ - write_rtc##type(); \ - break; \ - \ - case 0x09 ... 0x0C: \ - /* gamepak ROM space */ \ - break; \ - \ - case 0x0D: \ - write_eeprom(address, value); \ - break; \ - \ - case 0x0E: \ - write_backup##type(); \ - break; \ - } \ - -u8 function_cc read_memory8(u32 address) -{ - u8 value; - read_memory(8); - return value; -} - -u16 function_cc read_memory16_signed(u32 address) -{ - u16 value; - - if(address & 0x01) - { - return (s8)read_memory8(address); - } - else - { - read_memory(16); - } - - return value; -} - -// unaligned reads are actually 32bit - -u32 function_cc read_memory16(u32 address) -{ - u32 value; - - if(address & 0x01) - { - address &= ~0x01; - read_memory(16); - ror(value, value, 8); - } - else - { - read_memory(16); - } - - return value; -} - - -u32 function_cc read_memory32(u32 address) -{ - u32 value; - if(address & 0x03) - { - u32 rotate = (address & 0x03) * 8; - address &= ~0x03; - read_memory(32); - ror(value, value, rotate); - } - else - { - read_memory(32); - } - - return value; -} - -cpu_alert_type function_cc write_memory8(u32 address, u8 value) -{ - write_memory(8); - return CPU_ALERT_NONE; -} - -cpu_alert_type function_cc write_memory16(u32 address, u16 value) -{ - write_memory(16); - return CPU_ALERT_NONE; -} - -cpu_alert_type function_cc write_memory32(u32 address, u32 value) -{ - write_memory(32); - return CPU_ALERT_NONE; -} - -char backup_filename[512]; - -u32 load_backup(char *name) -{ - file_open(backup_file, name, read); - - if(file_check_valid(backup_file)) - { - u32 backup_size = file_length(name, backup_file); - - file_read(backup_file, gamepak_backup, backup_size); - - file_close(backup_file); - - // The size might give away what kind of backup it is. - switch(backup_size) - { - case 0x200: - backup_type = BACKUP_EEPROM; - eeprom_size = EEPROM_512_BYTE; - break; - - case 0x2000: - backup_type = BACKUP_EEPROM; - eeprom_size = EEPROM_8_KBYTE; - break; - - case 0x8000: - backup_type = BACKUP_SRAM; - sram_size = SRAM_SIZE_32KB; - break; - - // Could be either flash or SRAM, go with flash - case 0x10000: - backup_type = BACKUP_FLASH; - sram_size = FLASH_SIZE_64KB; - break; - - case 0x20000: - backup_type = BACKUP_FLASH; - flash_size = FLASH_SIZE_128KB; - break; - } - return 1; - } - else - { - backup_type = BACKUP_NONE; - memset(gamepak_backup, 0xFF, 1024 * 128); - } - - return 0; -} - -u32 save_backup(char *name) -{ - if(backup_type != BACKUP_NONE) - { - file_open(backup_file, name, write); - - if(file_check_valid(backup_file)) - { - u32 backup_size = 0; - - switch(backup_type) - { - case BACKUP_SRAM: - if(sram_size == SRAM_SIZE_32KB) - backup_size = 0x8000; - else - backup_size = 0x10000; - break; - - case BACKUP_FLASH: - if(flash_size == FLASH_SIZE_64KB) - backup_size = 0x10000; - else - backup_size = 0x20000; - break; - - case BACKUP_EEPROM: - if(eeprom_size == EEPROM_512_BYTE) - backup_size = 0x200; - else - backup_size = 0x2000; - break; - - default: - break; - } - - file_write(backup_file, gamepak_backup, backup_size); - - file_close(backup_file); - return 1; - } - } - - return 0; -} - -void update_backup() -{ - if(backup_update != (write_backup_delay + 1)) - backup_update--; - - if(backup_update == 0) - { - save_backup(backup_filename); - backup_update = write_backup_delay + 1; - } -} - -void update_backup_force() -{ - save_backup(backup_filename); -} - -#define CONFIG_FILENAME "game_config.txt" - -char *skip_spaces(char *line_ptr) -{ - while(*line_ptr == ' ') - line_ptr++; - - return line_ptr; -} - -s32 parse_config_line(char *current_line, char *current_variable, char *current_value) -{ - char *line_ptr = current_line; - char *line_ptr_new; - - if((current_line[0] == 0) || (current_line[0] == '#')) - return -1; - - line_ptr_new = strchr(line_ptr, ' '); - if(line_ptr_new == NULL) - return -1; - - *line_ptr_new = 0; - strcpy(current_variable, line_ptr); - line_ptr_new = skip_spaces(line_ptr_new + 1); - - if(*line_ptr_new != '=') - return -1; - - line_ptr_new = skip_spaces(line_ptr_new + 1); - strcpy(current_value, line_ptr_new); - line_ptr_new = current_value + strlen(current_value) - 1; - if(*line_ptr_new == '\n') - { - line_ptr_new--; - *line_ptr_new = 0; - } - - if(*line_ptr_new == '\r') - *line_ptr_new = 0; - - return 0; -} - -s32 load_game_config(char *gamepak_title, char *gamepak_code, char *gamepak_maker) -{ - char current_line[256]; - char current_variable[256]; - char current_value[256]; - char config_path[512]; - FILE *config_file; - - idle_loop_target_pc = 0xFFFFFFFF; - iwram_stack_optimize = 1; - bios_rom[0x39] = 0x00; - bios_rom[0x2C] = 0x00; - translation_gate_targets = 0; - flash_device_id = FLASH_DEVICE_MACRONIX_64KB; - - sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, CONFIG_FILENAME); - - config_file = fopen(config_path, "rb"); - - if(config_file) - { - while(fgets(current_line, 256, config_file)) - { - if(parse_config_line(current_line, current_variable, current_value) - != -1) - { - if(strcmp(current_variable, "game_name") || - strcmp(current_value, gamepak_title)) - continue; - - if(!fgets(current_line, 256, config_file) || - (parse_config_line(current_line, current_variable, - current_value) == -1) || - strcmp(current_variable, "game_code") || - strcmp(current_value, gamepak_code)) - continue; - - if(!fgets(current_line, 256, config_file) || - (parse_config_line(current_line, current_variable, - current_value) == -1) || - strcmp(current_variable, "vender_code") || - strcmp(current_value, gamepak_maker)) - continue; - - while(fgets(current_line, 256, config_file)) - { - if(parse_config_line(current_line, current_variable, current_value) - != -1) - { - if(!strcmp(current_variable, "game_name")) - { - fclose(config_file); - return 0; - } - - if(!strcmp(current_variable, "idle_loop_eliminate_target")) - idle_loop_target_pc = strtol(current_value, NULL, 16); - - if(!strcmp(current_variable, "translation_gate_target")) - { - if(translation_gate_targets < MAX_TRANSLATION_GATES) - { - translation_gate_target_pc[translation_gate_targets] = - strtol(current_value, NULL, 16); - translation_gate_targets++; - } - } - - if(!strcmp(current_variable, "iwram_stack_optimize") && - !strcmp(current_value, "no\0")) /* \0 for broken toolchain workaround */ - { - iwram_stack_optimize = 0; - } - - if(!strcmp(current_variable, "flash_rom_type") && - !strcmp(current_value, "128KB")) - { - flash_device_id = FLASH_DEVICE_MACRONIX_128KB; - } - - if(!strcmp(current_variable, "bios_rom_hack_39") && - !strcmp(current_value, "yes")) - { - bios_rom[0x39] = 0xC0; - } - - if(!strcmp(current_variable, "bios_rom_hack_2C") && - !strcmp(current_value, "yes")) - { - bios_rom[0x2C] = 0x02; - } - } - } - - fclose(config_file); - return 0; - } - } - - fclose(config_file); - } - -#ifndef PSP_BUILD - printf("game config missing\n"); -#endif - return -1; -} - -s32 load_gamepak_raw(const char *name) -{ - file_open(gamepak_file, name, read); - - if(file_check_valid(gamepak_file)) - { - u32 file_size = file_length(name, gamepak_file); - - // First, close the last one if it was open, we won't - // be needing it anymore. - if(file_check_valid(gamepak_file_large)) - file_close(gamepak_file_large); - - // If it's a big file size keep it, don't close it, we'll - // probably want to load from it more later. - if(file_size <= gamepak_ram_buffer_size) - { - file_read(gamepak_file, gamepak_rom, file_size); - - file_close(gamepak_file); - -#ifdef PSP_BUILD - gamepak_file_large = -1; -#else - gamepak_file_large = NULL; -#endif - } - else - { - // Read in just enough for the header - file_read(gamepak_file, gamepak_rom, 0x100); - gamepak_file_large = gamepak_file; - } - - return file_size; - } - - return -1; -} - -char gamepak_title[13]; -char gamepak_code[5]; -char gamepak_maker[3]; -char gamepak_filename[512]; - -u32 load_gamepak(const char *name) -{ - char cheats_filename[256]; - - s32 file_size = load_gamepak_raw(name); - - // A dumb April fool's joke was here once :o - - if(file_size != -1) - { - gamepak_size = (file_size + 0x7FFF) & ~0x7FFF; - - strncpy(gamepak_filename, name, sizeof(gamepak_filename)); - gamepak_filename[sizeof(gamepak_filename) - 1] = 0; - - make_rpath(backup_filename, sizeof(backup_filename), ".sav"); - if (!load_backup(backup_filename)) - { - // try path used by older versions - strcpy(backup_filename, name); - change_ext(gamepak_filename, backup_filename, ".sav"); - load_backup(backup_filename); - } - - memcpy(gamepak_title, gamepak_rom + 0xA0, 12); - memcpy(gamepak_code, gamepak_rom + 0xAC, 4); - memcpy(gamepak_maker, gamepak_rom + 0xB0, 2); - gamepak_title[12] = 0; - gamepak_code[4] = 0; - gamepak_maker[2] = 0; - - load_game_config(gamepak_title, gamepak_code, gamepak_maker); -#ifndef __LIBRETRO__ - load_game_config_file(); -#endif - - change_ext(gamepak_filename, cheats_filename, ".cht"); - add_cheats(cheats_filename); - - return 0; - } - - return -1; -} - -s32 load_bios(char *name) -{ - file_open(bios_file, name, read); - - if(file_check_valid(bios_file)) - { - file_read(bios_file, bios_rom, 0x4000); - - // This is a hack to get Zelda working, because emulating - // the proper memory read behavior here is much too expensive. - file_close(bios_file); - return 0; - } - - return -1; -} - -// DMA memory regions can be one of the following: -// IWRAM - 32kb offset from the contiguous iwram region. -// EWRAM - like segmented but with self modifying code check. -// VRAM - 96kb offset from the contiguous vram region, should take care -// Palette RAM - Converts palette entries when written to. -// OAM RAM - Sets OAM modified flag to true. -// I/O registers - Uses the I/O register function. -// of mirroring properly. -// Segmented RAM/ROM - a region >= 32kb, the translated address has to -// be reloaded if it wraps around the limit (cartride ROM) -// Ext - should be handled by the memory read/write function. - -// The following map determines the region of each (assumes DMA access -// is not done out of bounds) - -typedef enum -{ - DMA_REGION_IWRAM, - DMA_REGION_EWRAM, - DMA_REGION_VRAM, - DMA_REGION_PALETTE_RAM, - DMA_REGION_OAM_RAM, - DMA_REGION_IO, - DMA_REGION_GAMEPAK, - DMA_REGION_EXT, - DMA_REGION_BIOS, - DMA_REGION_NULL -} dma_region_type; - -dma_region_type dma_region_map[16] = -{ - DMA_REGION_BIOS, // 0x00 - BIOS - DMA_REGION_NULL, // 0x01 - Nothing - DMA_REGION_EWRAM, // 0x02 - EWRAM - DMA_REGION_IWRAM, // 0x03 - IWRAM - DMA_REGION_IO, // 0x04 - I/O registers - DMA_REGION_PALETTE_RAM, // 0x05 - palette RAM - DMA_REGION_VRAM, // 0x06 - VRAM - DMA_REGION_OAM_RAM, // 0x07 - OAM RAM - DMA_REGION_GAMEPAK, // 0x08 - gamepak ROM - DMA_REGION_GAMEPAK, // 0x09 - gamepak ROM - DMA_REGION_GAMEPAK, // 0x0A - gamepak ROM - DMA_REGION_GAMEPAK, // 0x0B - gamepak ROM - DMA_REGION_GAMEPAK, // 0x0C - gamepak ROM - DMA_REGION_EXT, // 0x0D - EEPROM - DMA_REGION_EXT, // 0x0E - gamepak SRAM/flash ROM - DMA_REGION_EXT // 0x0F - gamepak SRAM/flash ROM -}; - -#define dma_adjust_ptr_inc(ptr, size) \ - ptr += (size / 8) \ - -#define dma_adjust_ptr_dec(ptr, size) \ - ptr -= (size / 8) \ - -#define dma_adjust_ptr_fix(ptr, size) \ - -#define dma_adjust_ptr_writeback() \ - dma->dest_address = dest_ptr \ - -#define dma_adjust_ptr_reload() \ - -#define dma_print(src_op, dest_op, transfer_size, wb) \ - printf("dma from %x (%s) to %x (%s) for %x (%s) (%s) (%d) (pc %x)\n", \ - src_ptr, #src_op, dest_ptr, #dest_op, length, #transfer_size, #wb, \ - dma->irq, reg[15]); \ - -#define dma_smc_vars_src() \ - -#define dma_smc_vars_dest() \ - u32 smc_trigger = 0 \ - -#define dma_vars_iwram(type) \ - dma_smc_vars_##type() \ - -#define dma_vars_vram(type) \ - -#define dma_vars_palette_ram(type) \ - -#define dma_oam_ram_src() \ - -#define dma_oam_ram_dest() \ - oam_update = 1 \ - -#define dma_vars_oam_ram(type) \ - dma_oam_ram_##type() \ - -#define dma_vars_io(type) \ - -#define dma_segmented_load_src() \ - memory_map_read[src_current_region] \ - -#define dma_segmented_load_dest() \ - memory_map_write[dest_current_region] \ - -#define dma_vars_gamepak(type) \ - u32 type##_new_region; \ - u32 type##_current_region = type##_ptr >> 15; \ - u8 *type##_address_block = dma_segmented_load_##type(); \ - if(type##_address_block == NULL) \ - { \ - if((type##_ptr & 0x1FFFFFF) >= gamepak_size) \ - break; \ - type##_address_block = load_gamepak_page(type##_current_region & 0x3FF); \ - } \ - -#define dma_vars_ewram(type) \ - dma_smc_vars_##type(); \ - u32 type##_new_region; \ - u32 type##_current_region = type##_ptr >> 15; \ - u8 *type##_address_block = dma_segmented_load_##type() \ - -#define dma_vars_bios(type) \ - -#define dma_vars_ext(type) \ - -#define dma_ewram_check_region(type) \ - type##_new_region = (type##_ptr >> 15); \ - if(type##_new_region != type##_current_region) \ - { \ - type##_current_region = type##_new_region; \ - type##_address_block = dma_segmented_load_##type(); \ - } \ - -#define dma_gamepak_check_region(type) \ - type##_new_region = (type##_ptr >> 15); \ - if(type##_new_region != type##_current_region) \ - { \ - type##_current_region = type##_new_region; \ - type##_address_block = dma_segmented_load_##type(); \ - if(type##_address_block == NULL) \ - { \ - type##_address_block = \ - load_gamepak_page(type##_current_region & 0x3FF); \ - } \ - } \ - -#define dma_read_iwram(type, transfer_size) \ - read_value = address##transfer_size(iwram + 0x8000, type##_ptr & 0x7FFF) \ - -#define dma_read_vram(type, transfer_size) \ - read_value = address##transfer_size(vram, type##_ptr & 0x1FFFF) \ - -#define dma_read_io(type, transfer_size) \ - read_value = address##transfer_size(io_registers, type##_ptr & 0x7FFF) \ - -#define dma_read_oam_ram(type, transfer_size) \ - read_value = address##transfer_size(oam_ram, type##_ptr & 0x3FF) \ - -#define dma_read_palette_ram(type, transfer_size) \ - read_value = address##transfer_size(palette_ram, type##_ptr & 0x3FF) \ - -#define dma_read_ewram(type, transfer_size) \ - dma_ewram_check_region(type); \ - read_value = address##transfer_size(type##_address_block, \ - type##_ptr & 0x7FFF) \ - -#define dma_read_gamepak(type, transfer_size) \ - dma_gamepak_check_region(type); \ - read_value = address##transfer_size(type##_address_block, \ - type##_ptr & 0x7FFF) \ - -// DMAing from the BIOS is funny, just returns 0.. - -#define dma_read_bios(type, transfer_size) \ - read_value = 0 \ - -#define dma_read_ext(type, transfer_size) \ - read_value = read_memory##transfer_size(type##_ptr) \ - -#define dma_write_iwram(type, transfer_size) \ - address##transfer_size(iwram + 0x8000, type##_ptr & 0x7FFF) = read_value; \ - smc_trigger |= address##transfer_size(iwram, type##_ptr & 0x7FFF) \ - -#define dma_write_vram(type, transfer_size) \ - address##transfer_size(vram, type##_ptr & 0x1FFFF) = read_value \ - -#define dma_write_io(type, transfer_size) \ - write_io_register##transfer_size(type##_ptr & 0x3FF, read_value) \ - -#define dma_write_oam_ram(type, transfer_size) \ - address##transfer_size(oam_ram, type##_ptr & 0x3FF) = read_value \ - -#define dma_write_palette_ram(type, transfer_size) \ - write_palette##transfer_size(type##_ptr & 0x3FF, read_value) \ - -#define dma_write_ext(type, transfer_size) \ - write_memory##transfer_size(type##_ptr, read_value) \ - -#define dma_write_ewram(type, transfer_size) \ - dma_ewram_check_region(type); \ - \ - address##transfer_size(type##_address_block, type##_ptr & 0x7FFF) = \ - read_value; \ - smc_trigger |= address##transfer_size(type##_address_block, \ - (type##_ptr & 0x7FFF) - 0x8000) \ - -#define dma_epilogue_iwram() \ - if(smc_trigger) \ - { \ - /* Special return code indicating to retranslate to the CPU code */ \ - return_value = CPU_ALERT_SMC; \ - } \ - -#define dma_epilogue_ewram() \ - if(smc_trigger) \ - { \ - /* Special return code indicating to retranslate to the CPU code */ \ - return_value = CPU_ALERT_SMC; \ - } \ - -#define dma_epilogue_vram() \ - -#define dma_epilogue_io() \ - -#define dma_epilogue_oam_ram() \ - -#define dma_epilogue_palette_ram() \ - -#define dma_epilogue_GAMEPAK() \ - -#define dma_epilogue_ext() \ - -#define print_line() \ - dma_print(src_op, dest_op, transfer_size, wb); \ - -#define dma_transfer_loop_region(src_region_type, dest_region_type, src_op, \ - dest_op, transfer_size, wb) \ -{ \ - dma_vars_##src_region_type(src); \ - dma_vars_##dest_region_type(dest); \ - \ - for(i = 0; i < length; i++) \ - { \ - dma_read_##src_region_type(src, transfer_size); \ - dma_write_##dest_region_type(dest, transfer_size); \ - dma_adjust_ptr_##src_op(src_ptr, transfer_size); \ - dma_adjust_ptr_##dest_op(dest_ptr, transfer_size); \ - } \ - dma->source_address = src_ptr; \ - dma_adjust_ptr_##wb(); \ - dma_epilogue_##dest_region_type(); \ - break; \ -} \ - -#define dma_transfer_loop(src_op, dest_op, transfer_size, wb); \ -{ \ - u32 src_region = src_ptr >> 24; \ - u32 dest_region = dest_ptr >> 24; \ - dma_region_type src_region_type = dma_region_map[src_region]; \ - dma_region_type dest_region_type = dma_region_map[dest_region]; \ - \ - switch(src_region_type | (dest_region_type << 4)) \ - { \ - case (DMA_REGION_BIOS | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(bios, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(iwram, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(ewram, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(vram, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(palette_ram, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(oam_ram, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(io, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(gamepak, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_IWRAM << 4)): \ - dma_transfer_loop_region(ext, iwram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(bios, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(iwram, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(ewram, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(vram, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(palette_ram, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(oam_ram, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(io, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(gamepak, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_EWRAM << 4)): \ - dma_transfer_loop_region(ext, ewram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(bios, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(iwram, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(ewram, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(vram, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(palette_ram, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(oam_ram, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(io, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(gamepak, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_VRAM << 4)): \ - dma_transfer_loop_region(ext, vram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(bios, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(iwram, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(ewram, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(vram, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(palette_ram, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(oam_ram, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(io, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(gamepak, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_PALETTE_RAM << 4)): \ - dma_transfer_loop_region(ext, palette_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(bios, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(iwram, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(ewram, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(vram, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(palette_ram, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(oam_ram, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(io, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(gamepak, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_OAM_RAM << 4)): \ - dma_transfer_loop_region(ext, oam_ram, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(bios, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(iwram, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(ewram, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(vram, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(palette_ram, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(oam_ram, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(io, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(gamepak, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_IO << 4)): \ - dma_transfer_loop_region(ext, io, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_BIOS | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(bios, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IWRAM | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(iwram, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EWRAM | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(ewram, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_VRAM | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(vram, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_PALETTE_RAM | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(palette_ram, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_OAM_RAM | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(oam_ram, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_IO | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(io, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_GAMEPAK | (DMA_REGION_EXT << 4)): \ - dma_transfer_loop_region(gamepak, ext, src_op, dest_op, \ - transfer_size, wb); \ - \ - case (DMA_REGION_EXT | (DMA_REGION_EXT << 3)): \ - dma_transfer_loop_region(ext, ext, src_op, dest_op, \ - transfer_size, wb); \ - } \ - break; \ -} \ - -#define dma_transfer_expand(transfer_size) \ - switch((dma->dest_direction << 2) | dma->source_direction) \ - { \ - case 0x00: \ - dma_transfer_loop(inc, inc, transfer_size, writeback); \ - \ - case 0x01: \ - dma_transfer_loop(dec, inc, transfer_size, writeback); \ - \ - case 0x02: \ - dma_transfer_loop(fix, inc, transfer_size, writeback); \ - \ - case 0x03: \ - break; \ - \ - case 0x04: \ - dma_transfer_loop(inc, dec, transfer_size, writeback); \ - \ - case 0x05: \ - dma_transfer_loop(dec, dec, transfer_size, writeback); \ - \ - case 0x06: \ - dma_transfer_loop(fix, dec, transfer_size, writeback); \ - \ - case 0x07: \ - break; \ - \ - case 0x08: \ - dma_transfer_loop(inc, fix, transfer_size, writeback); \ - \ - case 0x09: \ - dma_transfer_loop(dec, fix, transfer_size, writeback); \ - \ - case 0x0A: \ - dma_transfer_loop(fix, fix, transfer_size, writeback); \ - \ - case 0x0B: \ - break; \ - \ - case 0x0C: \ - dma_transfer_loop(inc, inc, transfer_size, reload); \ - \ - case 0x0D: \ - dma_transfer_loop(dec, inc, transfer_size, reload); \ - \ - case 0x0E: \ - dma_transfer_loop(fix, inc, transfer_size, reload); \ - \ - case 0x0F: \ - break; \ - } \ - -cpu_alert_type dma_transfer(dma_transfer_type *dma) -{ - u32 i; - u32 length = dma->length; - u32 read_value; - u32 src_ptr = dma->source_address; - u32 dest_ptr = dma->dest_address; - cpu_alert_type return_value = CPU_ALERT_NONE; - - // Technically this should be done for source and destination, but - // chances are this is only ever used (probably mistakingly!) for dest. - // The only game I know of that requires this is Lucky Luke. - if((dest_ptr >> 24) != ((dest_ptr + length - 1) >> 24)) - { - u32 first_length = ((dest_ptr & 0xFF000000) + 0x1000000) - dest_ptr; - u32 second_length = length - first_length; - dma->length = first_length; - - dma_transfer(dma); - - dma->length = length; - - length = second_length; - dest_ptr += first_length; - src_ptr += first_length; - } - - if(dma->length_type == DMA_16BIT) - { - src_ptr &= ~0x01; - dest_ptr &= ~0x01; - cycle_dma16_words += length; - dma_transfer_expand(16); - } - else - { - src_ptr &= ~0x03; - dest_ptr &= ~0x03; - cycle_dma32_words += length; - dma_transfer_expand(32); - } - - if((dma->repeat_type == DMA_NO_REPEAT) || - (dma->start_type == DMA_START_IMMEDIATELY)) - { - dma->start_type = DMA_INACTIVE; - address16(io_registers, (dma->dma_channel * 12) + 0xBA) &= - (~0x8000); - } - - if(dma->irq) - { - raise_interrupt(IRQ_DMA0 << dma->dma_channel); - return_value = CPU_ALERT_IRQ; - } - - return return_value; -} - -// Be sure to do this after loading ROMs. - -#define map_region(type, start, end, mirror_blocks, region) \ - for(map_offset = (start) / 0x8000; map_offset < \ - ((end) / 0x8000); map_offset++) \ - { \ - memory_map_##type[map_offset] = \ - ((u8 *)region) + ((map_offset % mirror_blocks) * 0x8000); \ - } \ - -#define map_null(type, start, end) \ - for(map_offset = start / 0x8000; map_offset < (end / 0x8000); \ - map_offset++) \ - { \ - memory_map_##type[map_offset] = NULL; \ - } \ - -#define map_ram_region(type, start, end, mirror_blocks, region) \ - for(map_offset = (start) / 0x8000; map_offset < \ - ((end) / 0x8000); map_offset++) \ - { \ - memory_map_##type[map_offset] = \ - ((u8 *)region) + ((map_offset % mirror_blocks) * 0x10000) + 0x8000; \ - } \ - -#define map_vram(type) \ - for(map_offset = 0x6000000 / 0x8000; map_offset < (0x7000000 / 0x8000); \ - map_offset += 4) \ - { \ - memory_map_##type[map_offset] = vram; \ - memory_map_##type[map_offset + 1] = vram + 0x8000; \ - memory_map_##type[map_offset + 2] = vram + (0x8000 * 2); \ - memory_map_##type[map_offset + 3] = vram + (0x8000 * 2); \ - } \ - -#define map_vram_firstpage(type) \ - for(map_offset = 0x6000000 / 0x8000; map_offset < (0x7000000 / 0x8000); \ - map_offset += 4) \ - { \ - memory_map_##type[map_offset] = vram; \ - memory_map_##type[map_offset + 1] = NULL; \ - memory_map_##type[map_offset + 2] = NULL; \ - memory_map_##type[map_offset + 3] = NULL; \ - } \ - - -// Picks a page to evict -u32 page_time = 0; - -u32 evict_gamepak_page() -{ - // Find the one with the smallest frame timestamp - u32 page_index = 0; - u32 physical_index; - u32 smallest = gamepak_memory_map[0].page_timestamp; - u32 i; - - for(i = 1; i < gamepak_ram_pages; i++) - { - if(gamepak_memory_map[i].page_timestamp <= smallest) - { - smallest = gamepak_memory_map[i].page_timestamp; - page_index = i; - } - } - - physical_index = gamepak_memory_map[page_index].physical_index; - - memory_map_read[(0x8000000 / (32 * 1024)) + physical_index] = NULL; - memory_map_read[(0xA000000 / (32 * 1024)) + physical_index] = NULL; - memory_map_read[(0xC000000 / (32 * 1024)) + physical_index] = NULL; - - return page_index; -} - -u8 *load_gamepak_page(u32 physical_index) -{ - if(physical_index >= (gamepak_size >> 15)) - return gamepak_rom; - - u32 page_index = evict_gamepak_page(); - u32 page_offset = page_index * (32 * 1024); - u8 *swap_location = gamepak_rom + page_offset; - - gamepak_memory_map[page_index].page_timestamp = page_time; - gamepak_memory_map[page_index].physical_index = physical_index; - page_time++; - - file_seek(gamepak_file_large, physical_index * (32 * 1024), SEEK_SET); - file_read(gamepak_file_large, swap_location, (32 * 1024)); - memory_map_read[(0x8000000 / (32 * 1024)) + physical_index] = swap_location; - memory_map_read[(0xA000000 / (32 * 1024)) + physical_index] = swap_location; - memory_map_read[(0xC000000 / (32 * 1024)) + physical_index] = swap_location; - - // If RTC is active page the RTC register bytes so they can be read - if((rtc_state != RTC_DISABLED) && (physical_index == 0)) - { - memcpy(swap_location + 0xC4, rtc_registers, sizeof(rtc_registers)); - } - - return swap_location; -} - -void init_memory_gamepak() -{ - u32 map_offset = 0; - - if(gamepak_size > gamepak_ram_buffer_size) - { - // Large ROMs get special treatment because they - // can't fit into the 16MB ROM buffer. - u32 i; - for(i = 0; i < gamepak_ram_pages; i++) - { - gamepak_memory_map[i].page_timestamp = 0; - gamepak_memory_map[i].physical_index = 0; - } - - map_null(read, 0x8000000, 0xD000000); - } - else - { - map_region(read, 0x8000000, 0x8000000 + gamepak_size, 1024, gamepak_rom); - map_null(read, 0x8000000 + gamepak_size, 0xA000000); - map_region(read, 0xA000000, 0xA000000 + gamepak_size, 1024, gamepak_rom); - map_null(read, 0xA000000 + gamepak_size, 0xC000000); - map_region(read, 0xC000000, 0xC000000 + gamepak_size, 1024, gamepak_rom); - map_null(read, 0xC000000 + gamepak_size, 0xE000000); - } -} - -void init_gamepak_buffer() -{ - // Try to initialize 32MB (this is mainly for non-PSP platforms) - gamepak_rom = NULL; - - gamepak_ram_buffer_size = 32 * 1024 * 1024; - gamepak_rom = malloc(gamepak_ram_buffer_size); - - if(gamepak_rom == NULL) - { - // Try 16MB, for PSP, then lower in 2MB increments - gamepak_ram_buffer_size = 16 * 1024 * 1024; - gamepak_rom = malloc(gamepak_ram_buffer_size); - - while(gamepak_rom == NULL) - { - gamepak_ram_buffer_size -= (2 * 1024 * 1024); - gamepak_rom = malloc(gamepak_ram_buffer_size); - } - } - - // Here's assuming we'll have enough memory left over for this, - // and that the above succeeded (if not we're in trouble all around) - gamepak_ram_pages = gamepak_ram_buffer_size / (32 * 1024); - gamepak_memory_map = malloc(sizeof(gamepak_swap_entry_type) * - gamepak_ram_pages); -} - -void init_memory() -{ - u32 map_offset = 0; - - memory_regions[0x00] = (u8 *)bios_rom; - memory_regions[0x01] = (u8 *)bios_rom; - memory_regions[0x02] = (u8 *)ewram; - memory_regions[0x03] = (u8 *)iwram + 0x8000; - memory_regions[0x04] = (u8 *)io_registers; - memory_regions[0x05] = (u8 *)palette_ram; - memory_regions[0x06] = (u8 *)vram; - memory_regions[0x07] = (u8 *)oam_ram; - memory_regions[0x08] = (u8 *)gamepak_rom; - memory_regions[0x09] = (u8 *)(gamepak_rom + 0xFFFFFF); - memory_regions[0x0A] = (u8 *)gamepak_rom; - memory_regions[0x0B] = (u8 *)(gamepak_rom + 0xFFFFFF); - memory_regions[0x0C] = (u8 *)gamepak_rom; - memory_regions[0x0D] = (u8 *)(gamepak_rom + 0xFFFFFF); - memory_regions[0x0E] = (u8 *)gamepak_backup; - - memory_limits[0x00] = 0x3FFF; - memory_limits[0x01] = 0x3FFF; - memory_limits[0x02] = 0x3FFFF; - memory_limits[0x03] = 0x7FFF; - memory_limits[0x04] = 0x7FFF; - memory_limits[0x05] = 0x3FF; - memory_limits[0x06] = 0x17FFF; - memory_limits[0x07] = 0x3FF; - memory_limits[0x08] = 0x1FFFFFF; - memory_limits[0x09] = 0x1FFFFFF; - memory_limits[0x0A] = 0x1FFFFFF; - memory_limits[0x0B] = 0x1FFFFFF; - memory_limits[0x0C] = 0x1FFFFFF; - memory_limits[0x0D] = 0x1FFFFFF; - memory_limits[0x0E] = 0xFFFF; - - // Fill memory map regions, areas marked as NULL must be checked directly - map_region(read, 0x0000000, 0x1000000, 1, bios_rom); - map_null(read, 0x1000000, 0x2000000); - map_ram_region(read, 0x2000000, 0x3000000, 8, ewram); - map_ram_region(read, 0x3000000, 0x4000000, 1, iwram); - map_region(read, 0x4000000, 0x5000000, 1, io_registers); - map_null(read, 0x5000000, 0x6000000); - map_null(read, 0x6000000, 0x7000000); - map_vram(read); - map_null(read, 0x7000000, 0x8000000); - init_memory_gamepak(); - map_null(read, 0xE000000, 0x10000000); - - // Fill memory map regions, areas marked as NULL must be checked directly - map_null(write, 0x0000000, 0x2000000); - map_ram_region(write, 0x2000000, 0x3000000, 8, ewram); - map_ram_region(write, 0x3000000, 0x4000000, 1, iwram); - map_null(write, 0x4000000, 0x5000000); - map_null(write, 0x5000000, 0x6000000); - - // The problem here is that the current method of handling self-modifying code - // requires writeable memory to be proceeded by 32KB SMC data areas or be - // indirectly writeable. It's possible to get around this if you turn off the SMC - // check altogether, but this will make a good number of ROMs crash (perhaps most - // of the ones that actually need it? This has yet to be determined). - - // This is because VRAM cannot be efficiently made incontiguous, and still allow - // the renderer to work as efficiently. It would, at the very least, require a - // lot of hacking of the renderer which I'm not prepared to do. - - // However, it IS possible to directly map the first page no matter what because - // there's 32kb of blank stuff sitting beneath it. - if(direct_map_vram) - { - map_vram(write); - } - else - { - map_null(write, 0x6000000, 0x7000000); - } - - map_null(write, 0x7000000, 0x8000000); - map_null(write, 0x8000000, 0xE000000); - map_null(write, 0xE000000, 0x10000000); - - memset(io_registers, 0, 0x8000); - memset(oam_ram, 0, 0x400); - memset(palette_ram, 0, 0x400); - memset(iwram, 0, 0x10000); - memset(ewram, 0, 0x80000); - memset(vram, 0, 0x18000); - - io_registers[REG_DISPCNT] = 0x80; - io_registers[REG_P1] = 0x3FF; - io_registers[REG_BG2PA] = 0x100; - io_registers[REG_BG2PD] = 0x100; - io_registers[REG_BG3PA] = 0x100; - io_registers[REG_BG3PD] = 0x100; - io_registers[REG_RCNT] = 0x8000; - - backup_type = BACKUP_NONE; - - sram_size = SRAM_SIZE_32KB; - flash_size = FLASH_SIZE_64KB; - - flash_bank_ptr = gamepak_backup; - flash_command_position = 0; - eeprom_size = EEPROM_512_BYTE; - eeprom_mode = EEPROM_BASE_MODE; - eeprom_address = 0; - eeprom_counter = 0; - - flash_mode = FLASH_BASE_MODE; - - rtc_state = RTC_DISABLED; - memset(rtc_registers, 0, sizeof(rtc_registers)); - bios_read_protect = 0xe129f000; -} - -void memory_term(void) -{ - if (file_check_valid(gamepak_file_large)) - { - file_close(gamepak_file_large); - } - - if (gamepak_memory_map != NULL) - { - free(gamepak_memory_map); - gamepak_memory_map = NULL; - } - - if (gamepak_rom != NULL) - { - free(gamepak_rom); - gamepak_rom = NULL; - } -} - -void bios_region_read_allow() -{ - memory_map_read[0] = bios_rom; -} - -void bios_region_read_protect() -{ - memory_map_read[0] = NULL; -} - - -#define savestate_block(type) \ - cpu_##type##_savestate(savestate_file); \ - input_##type##_savestate(savestate_file); \ - main_##type##_savestate(savestate_file); \ - memory_##type##_savestate(savestate_file); \ - sound_##type##_savestate(savestate_file); \ - video_##type##_savestate(savestate_file) \ - -void gba_load_state(char *savestate_filename) -{ - file_open(savestate_file, savestate_filename, read); - if(file_check_valid(savestate_file)) - { - char current_gamepak_filename[512]; - u32 i; - u32 current_color; - - file_seek(savestate_file, (240 * 160 * 2) + sizeof(time_t), SEEK_SET); - - strcpy(current_gamepak_filename, gamepak_filename); - - savestate_block(read); - - file_close(savestate_file); - - flush_translation_cache_ram(); - flush_translation_cache_rom(); - flush_translation_cache_bios(); - - oam_update = 1; - gbc_sound_update = 1; - if(strcmp(current_gamepak_filename, gamepak_filename)) - { - u32 dot_position = strcspn(current_gamepak_filename, "."); - - // We'll let it slide if the filenames of the savestate and - // the gamepak are similar enough. - strcpy(gamepak_filename, current_gamepak_filename); - if(strncmp(savestate_filename, current_gamepak_filename, dot_position)) - { - if(load_gamepak(gamepak_filename) != -1) - { - reset_gba(); - // Okay, so this takes a while, but for now it works. - gba_load_state(savestate_filename); - } - else - { - quit(); - } - - return; - } - } - - for(i = 0; i < 512; i++) - { - current_color = palette_ram[i]; - palette_ram_converted[i] = - convert_palette(current_color); - } - - // Oops, these contain raw pointers - for(i = 0; i < 4; i++) - { - gbc_sound_channel[i].sample_data = square_pattern_duty[2]; - } - current_debug_state = STEP; - instruction_count = 0; - - reg[CHANGED_PC_STATUS] = 1; - } -} - -u8 savestate_write_buffer[506947]; -u8 *write_mem_ptr; - -void gba_save_state(char *savestate_filename, u16 *screen_capture) -{ - write_mem_ptr = savestate_write_buffer; - file_open(savestate_file, savestate_filename, write); - if(file_check_valid(savestate_file)) - { - time_t current_time; - file_write_mem(savestate_file, screen_capture, 240 * 160 * 2); - - time(¤t_time); - file_write_mem_variable(savestate_file, current_time); - - savestate_block(write_mem); - file_write(savestate_file, savestate_write_buffer, - sizeof(savestate_write_buffer)); - - file_close(savestate_file); - } -} - - -#define memory_savestate_builder(type) \ -void memory_##type##_savestate(file_tag_type savestate_file) \ -{ \ - u32 i; \ - \ - file_##type##_variable(savestate_file, backup_type); \ - file_##type##_variable(savestate_file, sram_size); \ - file_##type##_variable(savestate_file, flash_mode); \ - file_##type##_variable(savestate_file, flash_command_position); \ - file_##type##_variable(savestate_file, flash_bank_ptr); \ - file_##type##_variable(savestate_file, flash_device_id); \ - file_##type##_variable(savestate_file, flash_manufacturer_id); \ - file_##type##_variable(savestate_file, flash_size); \ - file_##type##_variable(savestate_file, eeprom_size); \ - file_##type##_variable(savestate_file, eeprom_mode); \ - file_##type##_variable(savestate_file, eeprom_address_length); \ - file_##type##_variable(savestate_file, eeprom_address); \ - file_##type##_variable(savestate_file, eeprom_counter); \ - file_##type##_variable(savestate_file, rtc_state); \ - file_##type##_variable(savestate_file, rtc_write_mode); \ - file_##type##_array(savestate_file, rtc_registers); \ - file_##type##_variable(savestate_file, rtc_command); \ - file_##type##_array(savestate_file, rtc_data); \ - file_##type##_variable(savestate_file, rtc_status); \ - file_##type##_variable(savestate_file, rtc_data_bytes); \ - file_##type##_variable(savestate_file, rtc_bit_count); \ - file_##type##_array(savestate_file, eeprom_buffer); \ - file_##type##_array(savestate_file, gamepak_filename); \ - file_##type##_array(savestate_file, dma); \ - \ - file_##type(savestate_file, iwram + 0x8000, 0x8000); \ - for(i = 0; i < 8; i++) \ - { \ - file_##type(savestate_file, ewram + (i * 0x10000) + 0x8000, 0x8000); \ - } \ - file_##type(savestate_file, vram, 0x18000); \ - file_##type(savestate_file, oam_ram, 0x400); \ - file_##type(savestate_file, palette_ram, 0x400); \ - file_##type(savestate_file, io_registers, 0x8000); \ - \ - /* This is a hack, for now. */ \ - if((flash_bank_ptr < gamepak_backup) || \ - (flash_bank_ptr > (gamepak_backup + (1024 * 64)))) \ - { \ - flash_bank_ptr = gamepak_backup; \ - } \ -} \ - -memory_savestate_builder(read); -memory_savestate_builder(write_mem); - -- cgit v1.2.3