nanoba-wasm/src/wasm/module.cpp
2024-06-19 20:20:17 +08:00

218 lines
5.3 KiB
C++

#include <core.hpp>
#include <nba/config.hpp>
#include <nba/rom/backup/eeprom.hpp>
#include <nba/rom/backup/sram.hpp>
#include <nba/rom/backup/flash.hpp>
#include <memory>
#include <vector>
#include <nba/log.hpp>
using Level = nba::Level;
using Key = nba::InputDevice::Key;
extern const u8 bios_data[16384];
static u8 *game_data;
static size_t data_size;
static void *output[4];
static nba::core::Core *vm = nullptr;
static float audios[4096*8];
static u32 backup_value[3]; // [0] 类型 [1] 容量 [2] 是否携带RTC
struct AnoVideoDevice: public nba::VideoDevice
{
u32 *buffer_memo = nullptr;
void Draw( u32 *buffer ) final
{
this->buffer_memo = buffer;
}
};
// 用BasicInputDevice进行修改
struct AnoInputDevice: public nba::InputDevice
{
void SetKeyStatus(Key key, bool pressed) {
key_status[static_cast<int>(key)] = pressed;
}
void PollOK( void ){
keypress_callback();
}
auto Poll(Key key) -> bool final {
return key_status[static_cast<int>(key)];
}
void SetOnChangeCallback(std::function<void(void)> callback) final {
keypress_callback = callback;
}
private:
std::function<void(void)> keypress_callback;
bool key_status[kKeyCount];
};
struct OutterDevices
{
std::shared_ptr<nba::VideoDevice> dev_v;
std::shared_ptr<nba::InputDevice> dev_i;
std::shared_ptr<nba::AudioDevice> dev_a;
};
static OutterDevices outter_dev;
void initVM()
{
if( vm == nullptr ){
// emu core
auto config = std::make_shared<nba::Config>();
config->skip_bios = true;
//setup
config->video_dev = outter_dev.dev_v = std::make_shared<AnoVideoDevice>();
config->input_dev = outter_dev.dev_i = std::make_shared<AnoInputDevice>();
//config->audio_dev = outter_dev.dev_a = std::make_shared<AnoAudioDevice>();
vm = new nba::core::Core( config );
// bios
std::vector<u8> bios( bios_data, bios_data+sizeof(bios_data) );
vm->Attach( bios );
}
}
extern "C" {
u8* getRom( size_t size )
{
if( size == 0 ) return game_data;
if( game_data == nullptr ){
game_data = (u8*)malloc(size);
if( game_data ) data_size = size;
}
else if( data_size != size ){
free(game_data);
game_data = nullptr;
game_data = (u8*)malloc(size);
if( game_data ) data_size = size;
}
return game_data;
}
u8* getIRam()
{
return vm->GetBus().memory.iram.data();
}
u8* getWRam()
{
return vm->GetBus().memory.wram.data();
}
u32 cyclePerFrame()
{
return (u32)nba::CoreBase::kCyclesPerFrame;
}
void** getOutput()
{
return output;
}
void run( u32 cycle, u32 input )
{
static u32 keys = 0;
if( input != keys ){
keys = input;
// setup input
auto p = outter_dev.dev_i.get();
auto i = static_cast<AnoInputDevice*>(p);
for( size_t k=0; k <= (size_t)Key::R; k++ )
i->SetKeyStatus( (Key)k, ( input & (1<<k) ) != 0 );
i->PollOK();
}
vm->Run( cycle );
auto v = static_cast<const AnoVideoDevice*>( outter_dev.dev_v.get() );
output[0] = v->buffer_memo;
auto buffer = vm->GetAPU().buffer;
if( buffer ){
auto avail = buffer->Available();
if( avail )
{
// read to audios
auto size = avail * 2;
float *p = audios;
for( int i=0; i < avail; ++i )
{
auto data = buffer->Read();
*p++ = data.left;
*p++ = data.right;
}
output[1] = (void*)size;
output[2] = audios;
}
else output[1] = output[2] = nullptr;
}
else output[1] = output[2] = nullptr;
vm->GetROM().Detect(backup_value[0], backup_value[1], backup_value[2]);
output[3] = backup_value;
}
void go( u32 framecount, u32 input )
{
initVM();
// rom
vm->Attach( nba::ROM(std::vector<u8>(game_data, game_data+data_size), nullptr, nullptr, 0xffff'ffff) );
// start
vm->Reset();
run(framecount * cyclePerFrame(), input);
}
void replay()
{
// new backup
auto backup = std::unique_ptr<nba::Backup>{};
auto gpio = std::unique_ptr<nba::GPIO>{};
if( backup_value[0] == 0 ) return;
vm->Reset();
if( backup_value[0] == 1 )
{
backup = std::make_unique<nba::SRAM>( fs::path{"/"} );
}
else if( backup_value[0] == 2 )
{
backup = std::make_unique<nba::FLASH>( fs::path{"/"}, backup_value[1] > 512*1024 ? nba::FLASH::Size::SIZE_128K : nba::FLASH::Size::SIZE_64K );
}
else if( backup_value[0] == 3 )
{
backup = std::make_unique<nba::EEPROM>( fs::path{"/"}, backup_value[1] > 8*1024 ? nba::EEPROM::Size::SIZE_64K : nba::EEPROM::Size::SIZE_4K, vm->GetScheduler() );
}
if( backup_value[2] ){
gpio->Attach( vm->CreateRTC() );
}
vm->Attach( nba::ROM(std::vector<u8>(game_data, game_data+data_size), std::move(backup), std::move(gpio), 0xffff'ffff) );
vm->Reset();
}
}
#if __EMSCRIPTEN__
int main(){
initVM();
return 0;
}
#endif