cog/Frameworks/lazyusf/lazyusf/usf.c

422 lines
11 KiB
C

#include <stdint.h>
#include <string.h>
#include "usf.h"
#include "cpu.h"
#include "memory.h"
#include "audio.h"
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
#include "resampler.h"
#include "usf_internal.h"
size_t usf_get_state_size() {
return sizeof(usf_state_t) + 8192;
}
void usf_clear(void * state) {
size_t offset;
memset(state, 0, usf_get_state_size());
offset = 4096 - (((uintptr_t)state) & 4095);
USF_STATE_HELPER->offset_to_structure = offset;
//USF_STATE->savestatespace = NULL;
//USF_STATE->cpu_running = 0;
USF_STATE->cpu_stopped = 1;
//USF_STATE->enablecompare = 0;
//USF_STATE->enableFIFOfull = 0;
//USF_STATE->enable_hle_audio = 0;
//USF_STATE->NextInstruction = 0;
//USF_STATE->JumpToLocation = 0;
//USF_STATE->AudioIntrReg = 0;
//USF_STATE->CPU_Action = 0;
//USF_STATE->Timers = 0;
//USF_STATE->CPURunning = 0;
//USF_STATE->SPHack = 0;
//USF_STATE->WaitMode = 0;
//USF_STATE->TLB_Map = 0;
//USF_STATE->MemChunk = 0;
USF_STATE->RdramSize = 0x800000;
USF_STATE->SystemRdramSize = 0x800000;
USF_STATE->RomFileSize = 0x4000000;
//USF_STATE->N64MEM = 0;
//USF_STATE->RDRAM = 0;
//USF_STATE->DMEM = 0;
//USF_STATE->IMEM = 0;
//memset(USF_STATE->ROMPages, 0, sizeof(USF_STATE->ROMPages));
//USF_STATE->savestatespace = 0;
//USF_STATE->NOMEM = 0;
//USF_STATE->WrittenToRom = 0;
//USF_STATE->WroteToRom = 0;
//USF_STATE->TempValue = 0;
//USF_STATE->MemoryState = 0;
//USF_STATE->EmptySpace = 0;
//USF_STATE->Registers = 0;
//USF_STATE->PIF_Ram = 0;
PreAllocate_Memory(USF_STATE);
USF_STATE->resampler = resampler_create();
#ifdef DEBUG_INFO
USF_STATE->debug_log = fopen("/tmp/lazyusf.log", "w");
#endif
}
void usf_set_compare(void * state, int enable) {
USF_STATE->enablecompare = enable;
}
void usf_set_fifo_full(void * state, int enable) {
USF_STATE->enableFIFOfull = enable;
}
void usf_set_hle_audio(void * state, int enable) {
USF_STATE->enable_hle_audio = enable;
}
static uint32_t get_le32( const void * _p ) {
const uint8_t * p = (const uint8_t *) _p;
return p[0] + p[1] * 0x100 + p[2] * 0x10000 + p[3] * 0x1000000;
}
int usf_upload_section(void * state, const uint8_t * data, size_t size) {
uint32_t temp;
if ( size < 4 ) return -1;
temp = get_le32( data ); data += 4; size -= 4;
if(temp == 0x34365253) { //there is a rom section
uint32_t len, start;
if ( size < 4 ) return -1;
len = get_le32( data ); data += 4; size -= 4;
while(len) {
if ( size < 4 ) return -1;
start = get_le32( data ); data += 4; size -= 4;
while(len) {
uint32_t page = start >> 16;
uint32_t readLen = ( ((start + len) >> 16) > page) ? (((page + 1) << 16) - start) : len;
if( USF_STATE->ROMPages[page] == 0 ) {
USF_STATE->ROMPages[page] = malloc(0x10000);
if ( USF_STATE->ROMPages[page] == 0 )
return -1;
memset(USF_STATE->ROMPages[page], 0, 0x10000);
}
if ( size < readLen )
return -1;
memcpy( USF_STATE->ROMPages[page] + (start & 0xffff), data, readLen );
data += readLen; size -= readLen;
start += readLen;
len -= readLen;
}
if ( size < 4 ) return -1;
len = get_le32( data ); data += 4; size -= 4;
}
}
if ( size < 4 ) return -1;
temp = get_le32( data ); data += 4; size -= 4;
if(temp == 0x34365253) {
uint32_t len, start;
if ( size < 4 ) return -1;
len = get_le32( data ); data += 4; size -= 4;
while(len) {
if ( size < 4 ) return -1;
start = get_le32( data ); data += 4; size -= 4;
if ( size < len ) return -1;
memcpy( USF_STATE->savestatespace + start, data, len );
data += len; size -= len;
if ( size < 4 ) return -1;
len = get_le32( data ); data += 4; size -= 4;
}
}
return 0;
}
static int is_valid_rom(const unsigned char *buffer) {
/* Test if rom is a native .z64 image with header 0x80371240. [ABCD] */
if((buffer[0]==0x80)&&(buffer[1]==0x37)&&(buffer[2]==0x12)&&(buffer[3]==0x40))
return 1;
/* Test if rom is a byteswapped .v64 image with header 0x37804012. [BADC] */
else if((buffer[0]==0x37)&&(buffer[1]==0x80)&&(buffer[2]==0x40)&&(buffer[3]==0x12))
return 1;
/* Test if rom is a wordswapped .n64 image with header 0x40123780. [DCBA] */
else if((buffer[0]==0x40)&&(buffer[1]==0x12)&&(buffer[2]==0x37)&&(buffer[3]==0x80))
return 1;
else
return 0;
}
static void swap_rom(const unsigned char* signature, unsigned char* localrom, int loadlength) {
unsigned char temp;
int i;
/* Btyeswap if .v64 image. */
if(signature[0]==0x37) {
for (i = 0; i < loadlength; i+=2) {
temp=localrom[i];
localrom[i]=localrom[i+1];
localrom[i+1]=temp;
}
}
/* Wordswap if .n64 image. */
else if(signature[0]==0x40) {
for (i = 0; i < loadlength; i+=4) {
temp=localrom[i];
localrom[i]=localrom[i+3];
localrom[i+3]=temp;
temp=localrom[i+1];
localrom[i+1]=localrom[i+2];
localrom[i+2]=temp;
}
}
}
static _system_type rom_country_code_to_system_type(unsigned short country_code) {
switch (country_code & 0xFF) {
// PAL codes
case 0x44:
case 0x46:
case 0x49:
case 0x50:
case 0x53:
case 0x55:
case 0x58:
case 0x59:
return SYSTEM_PAL;
// NTSC codes
case 0x37:
case 0x41:
case 0x45:
case 0x4a:
default: // Fallback for unknown codes
return SYSTEM_NTSC;
}
}
// Get the VI (vertical interrupt) limit associated to a ROM system type.
static int rom_system_type_to_vi_limit(_system_type system_type) {
switch (system_type) {
case SYSTEM_PAL:
case SYSTEM_MPAL:
return 50;
case SYSTEM_NTSC:
default:
return 60;
}
}
static int rom_system_type_to_ai_dac_rate(_system_type system_type) {
switch (system_type) {
case SYSTEM_PAL:
return 49656530;
case SYSTEM_MPAL:
return 48628316;
case SYSTEM_NTSC:
default:
return 48681812;
}
}
void open_rom_header(usf_state_t * state, unsigned char * header, int header_size) {
if (header_size >= sizeof(_rom_header))
memcpy(&state->ROM_HEADER, header, sizeof(_rom_header));
if (is_valid_rom((const unsigned char *)&state->ROM_HEADER))
swap_rom((const unsigned char *)&state->ROM_HEADER, (unsigned char *)&state->ROM_HEADER, sizeof(_rom_header));
/* add some useful properties to ROM_PARAMS */
state->ROM_PARAMS.systemtype = rom_country_code_to_system_type(state->ROM_HEADER.Country_code);
state->ROM_PARAMS.vilimit = rom_system_type_to_vi_limit(state->ROM_PARAMS.systemtype);
state->ROM_PARAMS.aidacrate = rom_system_type_to_ai_dac_rate(state->ROM_PARAMS.systemtype);
state->ROM_PARAMS.countperop = COUNT_PER_OP_DEFAULT;
}
static int usf_startup(usf_state_t * state) {
// Detect region
open_rom_header(state, state->savestatespace + 8, sizeof(_rom_header));
// Detect the Ramsize before the memory allocation
if(get_le32(state->savestatespace + 4) == 0x400000) {
void * savestate;
state->RdramSize = 0x400000;
savestate = realloc(state->savestatespace, 0x40275c);
if ( savestate )
state->savestatespace = savestate;
} else if(get_le32(USF_STATE->savestatespace + 4) == 0x800000)
state->RdramSize = 0x800000;
if ( !Allocate_Memory(state) )
return -1;
StartEmulationFromSave(state, USF_STATE->savestatespace);
return 0;
}
const char * usf_render(void * state, int16_t * buffer, size_t count, int32_t * sample_rate) {
USF_STATE->last_error = 0;
USF_STATE->error_message[0] = '\0';
if ( !USF_STATE->MemoryState ) {
if ( usf_startup( USF_STATE ) < 0 )
return USF_STATE->last_error;
}
if ( USF_STATE->samples_in_buffer ) {
size_t do_max = USF_STATE->samples_in_buffer;
if ( do_max > count )
do_max = count;
if ( buffer )
memcpy( buffer, USF_STATE->samplebuf, sizeof(int16_t) * 2 * do_max );
USF_STATE->samples_in_buffer -= do_max;
if ( sample_rate )
*sample_rate = USF_STATE->SampleRate;
if ( USF_STATE->samples_in_buffer ) {
memmove( USF_STATE->samplebuf, USF_STATE->samplebuf + do_max * 2, sizeof(int16_t) * 2 * USF_STATE->samples_in_buffer );
return 0;
}
if ( buffer )
buffer += 2 * do_max;
count -= do_max;
}
USF_STATE->sample_buffer = buffer;
USF_STATE->sample_buffer_count = count;
USF_STATE->cpu_stopped = 0;
USF_STATE->cpu_running = 1;
StartInterpreterCPU(USF_STATE);
if ( sample_rate )
*sample_rate = USF_STATE->SampleRate;
return USF_STATE->last_error;
}
const char * usf_render_resampled(void * state, int16_t * buffer, size_t count, int32_t sample_rate) {
if ( !buffer ) {
unsigned long samples_buffered = resampler_get_sample_count( USF_STATE->resampler );
resampler_clear(USF_STATE->resampler);
if (samples_buffered) {
unsigned long samples_to_remove = samples_buffered;
if (samples_to_remove > count)
samples_to_remove = count;
count -= samples_to_remove;
while (samples_to_remove--)
resampler_remove_sample(USF_STATE->resampler);
if (!count)
return 0;
}
count = (size_t)((uint64_t)count * USF_STATE->SampleRate / sample_rate);
if (count > USF_STATE->samples_in_buffer_2) {
count -= USF_STATE->samples_in_buffer_2;
USF_STATE->samples_in_buffer_2 = 0;
}
else if (count) {
USF_STATE->samples_in_buffer_2 -= count;
memmove(USF_STATE->samplebuf2, USF_STATE->samplebuf2 + 8192 - USF_STATE->samples_in_buffer_2 * 2, USF_STATE->samples_in_buffer_2 * sizeof(short) * 2);
return 0;
}
return usf_render(state, buffer, count, NULL);
}
while ( count ) {
const char * err;
while ( USF_STATE->samples_in_buffer_2 && resampler_get_free_count(USF_STATE->resampler) ) {
int i = 0, j = resampler_get_free_count(USF_STATE->resampler);
if (j > USF_STATE->samples_in_buffer_2)
j = (int)USF_STATE->samples_in_buffer_2;
for (i = 0; i < j; ++i) {
resampler_write_sample(USF_STATE->resampler, USF_STATE->samplebuf2[i*2], USF_STATE->samplebuf2[i*2+1]);
}
if (i) {
memmove(USF_STATE->samplebuf2, USF_STATE->samplebuf2 + i * 2, (USF_STATE->samples_in_buffer_2 - i) * sizeof(short) * 2);
USF_STATE->samples_in_buffer_2 -= i;
}
}
while ( count && resampler_get_sample_count(USF_STATE->resampler) ) {
resampler_get_sample(USF_STATE->resampler, buffer, buffer + 1);
resampler_remove_sample(USF_STATE->resampler);
buffer += 2;
--count;
}
if (!count)
break;
if (USF_STATE->samples_in_buffer_2)
continue;
err = usf_render(state, USF_STATE->samplebuf2, 4096, 0);
if (err)
return err;
USF_STATE->samples_in_buffer_2 = 4096;
resampler_set_rate(USF_STATE->resampler, (float)USF_STATE->SampleRate / (float)sample_rate);
}
return 0;
}
void usf_restart(void * state) {
if ( USF_STATE->MemoryState )
StartEmulationFromSave(USF_STATE, USF_STATE->savestatespace);
USF_STATE->samples_in_buffer = 0;
USF_STATE->samples_in_buffer_2 = 0;
resampler_clear(USF_STATE->resampler);
}
void usf_shutdown(void * state) {
Release_Memory(USF_STATE);
resampler_delete(USF_STATE->resampler);
#ifdef DEBUG_INFO
fclose(USF_STATE->debug_log);
#endif
}