506 lines
12 KiB
C
506 lines
12 KiB
C
/*******************************************************************************
|
|
* NewOswan
|
|
* ws.c: Base wonderswan implementation
|
|
* Based on the original Oswan-unix
|
|
* Copyright (c) 2014-2021 986-Studio. All rights reserved.
|
|
*
|
|
******************************************************************************/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// 07.04.2002: speed problems partially fixed
|
|
// 13.04.2002: Set cycles by line to 256 (according to toshi)
|
|
// this seems to work well in most situations with
|
|
// the new nec v30 cpu core
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include "log.h"
|
|
#include "rom.h"
|
|
#include "./nec/nec.h"
|
|
#include "./nec/necintrf.h"
|
|
#include "memory.h"
|
|
#include "gpu.h"
|
|
#include "io.h"
|
|
#include "audio.h"
|
|
#include "ws.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
uint32_t ws_cycles;
|
|
uint32_t ws_skip;
|
|
uint32_t ws_cyclesByLine = 0;
|
|
uint32_t vblank_count = 0;
|
|
|
|
char *ws_sram_path = NULL;
|
|
char *ws_ieep_path = NULL;
|
|
char *ws_rom_path = NULL;
|
|
wssystem_t systemType;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void ws_patchRom(void)
|
|
{
|
|
|
|
uint8_t *rom = memory_getRom();
|
|
uint32_t romSize = memory_getRomSize();
|
|
|
|
Log(TLOG_DEBUG, "ws", "developper Id: 0x%.2x", rom[romSize - 10]);
|
|
Log(TLOG_DEBUG, "ws", "Game Id: 0x%.2x", rom[romSize - 8]);
|
|
|
|
if (!ws_cyclesByLine)
|
|
{
|
|
ws_cyclesByLine = 256;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int ws_init(char *rompath)
|
|
{
|
|
uint8_t *rom;
|
|
uint32_t romSize;
|
|
|
|
if ((rom = ws_rom_load(rompath, &romSize)) == NULL)
|
|
{
|
|
Log(TLOG_PANIC, "ws", "Error: cannot load %s", rompath);
|
|
return (0);
|
|
}
|
|
|
|
ws_staticRam = (uint8_t *)load_file(ws_sram_path);
|
|
if (ws_staticRam == NULL)
|
|
{
|
|
ws_staticRam = (uint8_t *)create_file(ws_sram_path, 0x10000);
|
|
}
|
|
|
|
if (ws_staticRam == NULL)
|
|
{
|
|
Log(TLOG_PANIC, "ws", "Card SRAM load error!\n");
|
|
return 0;
|
|
}
|
|
|
|
externalEeprom = (uint8_t *)load_file(ws_ieep_path);
|
|
if (externalEeprom == NULL)
|
|
{
|
|
externalEeprom = (uint8_t *)create_file(ws_ieep_path, 0x100000);
|
|
}
|
|
if (externalEeprom == NULL)
|
|
{
|
|
Log(TLOG_PANIC, "ws", "Card EEPROM load error!\n");
|
|
return 0;
|
|
}
|
|
|
|
ws_memory_init(rom, romSize);
|
|
ws_patchRom();
|
|
|
|
ws_io_init();
|
|
ws_audio_init();
|
|
ws_gpu_init();
|
|
|
|
if (ws_rotated())
|
|
{
|
|
ws_io_flipControls();
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void ws_reset(void)
|
|
{
|
|
ws_memory_reset();
|
|
ws_io_reset();
|
|
ws_audio_reset();
|
|
ws_gpu_reset();
|
|
nec_reset(NULL);
|
|
nec_set_reg(NEC_SP, 0x2000);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int ws_executeLine(int16_t *framebuffer, int renderLine)
|
|
{
|
|
int drawWholeScreen = 0;
|
|
|
|
ws_audio_process();
|
|
|
|
// update scanline register
|
|
ws_ioRam[2] = ws_gpu_scanline;
|
|
|
|
/* Why twice like that and random cycle count???? */
|
|
ws_cycles = nec_execute((ws_cyclesByLine >> 1) + (rand() & 7));
|
|
ws_cycles += nec_execute((ws_cyclesByLine >> 1) + (rand() & 7));
|
|
|
|
if (ws_cycles >= ws_cyclesByLine + ws_cyclesByLine)
|
|
{
|
|
ws_skip = ws_cycles / ws_cyclesByLine;
|
|
}
|
|
else
|
|
{
|
|
ws_skip = 1;
|
|
}
|
|
|
|
ws_cycles %= ws_cyclesByLine;
|
|
|
|
for (uint32_t uI = 0 ; uI < ws_skip ; uI++)
|
|
{
|
|
if (renderLine)
|
|
{
|
|
ws_gpu_renderScanline(framebuffer);
|
|
}
|
|
|
|
ws_gpu_scanline++;
|
|
|
|
if (ws_gpu_scanline == 144)
|
|
{
|
|
drawWholeScreen = 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (ws_gpu_scanline > 158)
|
|
{
|
|
ws_gpu_scanline = 0;
|
|
{
|
|
if ((ws_ioRam[0xb2] & 32)) /* VBLANK Timer */
|
|
{
|
|
/* TODO: REPAIR THAT SHIT */
|
|
ws_ioRam[0xb6] &= ~32;
|
|
nec_int((ws_ioRam[0xb0] + 5) * 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
ws_ioRam[2] = ws_gpu_scanline;
|
|
|
|
if (drawWholeScreen)
|
|
{
|
|
|
|
if (ws_ioRam[0xb2] & 64) /* VBLANK INT */
|
|
{
|
|
ws_ioRam[0xb6] &= ~64;
|
|
nec_int((ws_ioRam[0xb0] + 6) * 4);
|
|
}
|
|
|
|
vblank_count++;
|
|
}
|
|
|
|
if (ws_ioRam[0xa4] && (ws_ioRam[0xb2] & 128)) /*HBLANK TMR*/
|
|
{
|
|
/* TODO: Check that shit */
|
|
if (!ws_ioRam[0xa5])
|
|
{
|
|
ws_ioRam[0xa5] = ws_ioRam[0xa4];
|
|
}
|
|
|
|
if (ws_ioRam[0xa5])
|
|
{
|
|
ws_ioRam[0xa5]--;
|
|
}
|
|
|
|
if ((!ws_ioRam[0xa5]) && (ws_ioRam[0xb2] & 128))
|
|
{
|
|
|
|
ws_ioRam[0xb6] &= ~128;
|
|
nec_int((ws_ioRam[0xb0] + 7) * 4);
|
|
}
|
|
}
|
|
|
|
if ((ws_ioRam[0x2] == ws_ioRam[0x3]) && (ws_ioRam[0xb2] & 16)) /*SCANLINE INT*/
|
|
{
|
|
ws_ioRam[0xb6] &= ~16;
|
|
nec_int((ws_ioRam[0xb0] + 4) * 4);
|
|
}
|
|
|
|
return (drawWholeScreen);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void ws_done(void)
|
|
{
|
|
ws_memory_done();
|
|
ws_io_done();
|
|
ws_audio_done();
|
|
ws_gpu_done();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void ws_set_system(wssystem_t system)
|
|
{
|
|
if (system == WS_SYSTEM_AUTODETECT)
|
|
{
|
|
system = WS_SYSTEM_CRYSTAL;
|
|
}
|
|
systemType = system;
|
|
}
|
|
|
|
wssystem_t ws_get_system()
|
|
{
|
|
return systemType;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#define MacroLoadNecRegisterFromFile(F, R) \
|
|
read(fp,&value,sizeof(value)); \
|
|
nec_set_reg(R,value);
|
|
|
|
int ws_loadState(char *statepath)
|
|
{
|
|
Log(TLOG_NORMAL, "ws", "loading %s\n", statepath);
|
|
uint16_t crc = memory_getRomCrc();
|
|
uint16_t newCrc;
|
|
unsigned value;
|
|
uint8_t ws_newVideoMode;
|
|
|
|
int fp = open(statepath, O_RDONLY);
|
|
|
|
if (fp == -1)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
read(fp, &newCrc, 2);
|
|
|
|
if (newCrc != crc)
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
MacroLoadNecRegisterFromFile(fp, NEC_IP);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_AW);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_BW);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_CW);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_DW);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_CS);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_DS);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_ES);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_SS);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_IX);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_IY);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_BP);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_SP);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_FLAGS);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_VECTOR);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_PENDING);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_NMI_STATE);
|
|
MacroLoadNecRegisterFromFile(fp, NEC_IRQ_STATE);
|
|
|
|
read(fp, internalRam, 65536);
|
|
read(fp, ws_staticRam, 65536);
|
|
read(fp, ws_ioRam, 256);
|
|
read(fp, ws_paletteColors, 8);
|
|
read(fp, ws_palette, 16 * 4 * 2);
|
|
read(fp, wsc_palette, 16 * 16 * 2);
|
|
read(fp, &ws_newVideoMode, 1);
|
|
read(fp, &ws_gpu_scanline, 1);
|
|
read(fp, externalEeprom, 131072);
|
|
|
|
ws_audio_readState(fp);
|
|
close(fp);
|
|
|
|
// force a video mode change to make all tiles dirty
|
|
ws_gpu_clearCache();
|
|
return (1);
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#define MacroStoreNecRegisterToFile(F, R) \
|
|
value=nec_get_reg(R); \
|
|
write(fp,&value,sizeof(value));
|
|
|
|
int ws_saveState(char *statepath)
|
|
{
|
|
uint16_t crc = memory_getRomCrc();
|
|
uint32_t value;
|
|
char newPath[1024];
|
|
Log(TLOG_DEBUG, "ws", "saving %s\n", statepath);
|
|
|
|
if (strlen(statepath) < 4)
|
|
{
|
|
sprintf(newPath, "%s.wss", statepath);
|
|
}
|
|
else
|
|
{
|
|
int len = strlen(statepath);
|
|
|
|
if ((statepath[len - 1] != 's') && (statepath[len - 1] != 'S'))
|
|
{
|
|
sprintf(newPath, "%s.wss", statepath);
|
|
}
|
|
else if ((statepath[len - 2] != 's') && (statepath[len - 2] != 'S'))
|
|
{
|
|
sprintf(newPath, "%s.wss", statepath);
|
|
}
|
|
else if ((statepath[len - 3] != 'w') && (statepath[len - 3] != 'w'))
|
|
{
|
|
sprintf(newPath, "%s.wss", statepath);
|
|
}
|
|
else if (statepath[len - 4] != '.')
|
|
{
|
|
sprintf(newPath, "%s.wss", statepath);
|
|
}
|
|
else
|
|
{
|
|
sprintf(newPath, "%s", statepath);
|
|
}
|
|
}
|
|
|
|
int fp = open(newPath, O_RDWR | O_CREAT, 0644);
|
|
|
|
if (fp == -1)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
write(fp, &crc, 2);
|
|
MacroStoreNecRegisterToFile(fp, NEC_IP);
|
|
MacroStoreNecRegisterToFile(fp, NEC_AW);
|
|
MacroStoreNecRegisterToFile(fp, NEC_BW);
|
|
MacroStoreNecRegisterToFile(fp, NEC_CW);
|
|
MacroStoreNecRegisterToFile(fp, NEC_DW);
|
|
MacroStoreNecRegisterToFile(fp, NEC_CS);
|
|
MacroStoreNecRegisterToFile(fp, NEC_DS);
|
|
MacroStoreNecRegisterToFile(fp, NEC_ES);
|
|
MacroStoreNecRegisterToFile(fp, NEC_SS);
|
|
MacroStoreNecRegisterToFile(fp, NEC_IX);
|
|
MacroStoreNecRegisterToFile(fp, NEC_IY);
|
|
MacroStoreNecRegisterToFile(fp, NEC_BP);
|
|
MacroStoreNecRegisterToFile(fp, NEC_SP);
|
|
MacroStoreNecRegisterToFile(fp, NEC_FLAGS);
|
|
MacroStoreNecRegisterToFile(fp, NEC_VECTOR);
|
|
MacroStoreNecRegisterToFile(fp, NEC_PENDING);
|
|
MacroStoreNecRegisterToFile(fp, NEC_NMI_STATE);
|
|
MacroStoreNecRegisterToFile(fp, NEC_IRQ_STATE);
|
|
|
|
write(fp, internalRam, 65536);
|
|
write(fp, ws_staticRam, 65536);
|
|
write(fp, ws_ioRam, 256);
|
|
write(fp, ws_paletteColors, 8);
|
|
write(fp, ws_palette, 16 * 4 * 2);
|
|
write(fp, wsc_palette, 16 * 16 * 2);
|
|
write(fp, &ws_videoMode, 1);
|
|
write(fp, &ws_gpu_scanline, 1);
|
|
write(fp, externalEeprom, 131072);
|
|
|
|
ws_audio_writeState(fp);
|
|
close(fp);
|
|
|
|
return (1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int ws_rotated(void)
|
|
{
|
|
uint8_t *rom = memory_getRom();
|
|
uint32_t romSize = memory_getRomSize();
|
|
|
|
return (rom[romSize - 4] & 1);
|
|
}
|