422 lines
11 KiB
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
|
|
}
|
|
|