Merge NeoGeo support code.

This commit is contained in:
sorgelig
2019-07-20 10:37:08 +08:00
parent 05022db309
commit d8c338046a
11 changed files with 975 additions and 42 deletions

View File

@@ -31,6 +31,7 @@ CPP_SRC = $(wildcard *.cpp) \
$(wildcard ./support/st/*.cpp) \
$(wildcard ./support/x86/*.cpp) \
$(wildcard ./support/snes/*.cpp) \
$(wildcard ./support/neogeo/*.cpp) \
lib/lodepng/lodepng.cpp
IMG = $(wildcard *.png)

View File

@@ -78,6 +78,8 @@
<ClCompile Include="support\minimig\minimig_config.cpp" />
<ClCompile Include="support\minimig\minimig_fdd.cpp" />
<ClCompile Include="support\minimig\minimig_hdd.cpp" />
<ClCompile Include="support\neogeo\graphics.cpp" />
<ClCompile Include="support\neogeo\loader.cpp" />
<ClCompile Include="support\sharpmz\sharpmz.cpp" />
<ClCompile Include="support\snes\snes.cpp" />
<ClCompile Include="support\st\st_ikbd.cpp" />
@@ -128,6 +130,8 @@
<ClInclude Include="support\minimig\minimig_fdd.h" />
<ClInclude Include="support\minimig\minimig_hdd.h" />
<ClInclude Include="support\minimig\minimig_hdd_internal.h" />
<ClInclude Include="support\neogeo\graphics.h" />
<ClInclude Include="support\neogeo\loader.h" />
<ClInclude Include="support\sharpmz\sharpmz.h" />
<ClInclude Include="support\snes\snes.h" />
<ClInclude Include="support\st\st_ikbd.h" />

View File

@@ -142,6 +142,12 @@
<ClCompile Include="video.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="support\neogeo\graphics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="support\neogeo\loader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="battery.h">
@@ -288,5 +294,11 @@
<ClInclude Include="lib\imlib2\Imlib2.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="support\neogeo\graphics.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="support\neogeo\loader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -1574,10 +1574,25 @@ void HandleUI(void)
case MENU_8BIT_MAIN_FILE_SELECTED:
printf("File selected: %s\n", SelectedPath);
user_io_store_filename(SelectedPath);
user_io_file_tx(SelectedPath, user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | ioctl_index, opensave);
if(user_io_use_cheats()) cheats_init(SelectedPath, user_io_get_file_crc());
menustate = MENU_NONE1;
if (is_neogeo_core())
{
if (!neogeo_romset_tx(SelectedPath))
{
OsdSetTitle("Message", 0);
OsdEnable(0); // do not disable keyboard
menu_timer = GetTimer(2000);
menustate = MENU_INFO;
} else {
menustate = MENU_NONE1;
}
}
else
{
user_io_store_filename(SelectedPath);
user_io_file_tx(SelectedPath, user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | ioctl_index, opensave);
if (user_io_use_cheats()) cheats_init(SelectedPath, user_io_get_file_crc());
menustate = MENU_NONE1;
}
break;
case MENU_8BIT_MAIN_IMAGE_SELECTED:
@@ -1591,6 +1606,21 @@ void HandleUI(void)
user_io_set_index(user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | (menusub + 1));
user_io_file_mount(SelectedPath, drive_num);
}
if (is_neogeo_core())
{
// ElectronAsh.
strcpy(SelectedPath + strlen(SelectedPath) - 3, "CUE");
printf("Checking for presence of CUE file %s\n", SelectedPath);
if (user_io_file_mount(SelectedPath, 1))
{
printf("CUE file found and mounted.\n");
parse_cue_file();
char str[2] = "";
neogeo_romset_tx(str);
}
}
menustate = SelectedPath[0] ? MENU_NONE1 : MENU_8BIT_MAIN1;
break;

View File

@@ -19,3 +19,6 @@
// SNES support
#include "support/snes/snes.h"
// NeoGeo support
#include "support/neogeo/loader.h"

View File

@@ -0,0 +1,79 @@
// Moves bytes around so that the fix graphics are stored in a way that
// takes advantage of the SDRAM 16-bit bus
// Part of Neogeo_MiSTer
// (C) 2019 Sean 'furrtek' Gonsalves
#include "graphics.h"
#include "../../spi.h"
void spr_convert(uint8_t* buf_in, uint8_t* buf_out, unsigned int size)
{
/*
In C ROMs, a word provides two bitplanes for an 8-pixel wide line
They're used in pairs to provide 32 bits at once (all four bitplanes)
For one sprite tile, bytes are used like this: ([...] represents one 8-pixel wide line)
Even ROM Odd ROM
[ 40 41 ][ 00 01 ] [ 42 43 ][ 02 03 ]
[ 44 45 ][ 04 05 ] [ 46 47 ][ 06 07 ]
[ 48 49 ][ 08 09 ] [ 4A 4B ][ 0A 0B ]
[ 4C 4D ][ 0C 0D ] [ 4E 4F ][ 0E 0F ]
[ 50 51 ][ 10 11 ] [ 52 53 ][ 12 13 ]
... ...
The data read for a given tile line (16 pixels) is always the same, only the rendering order of the pixels can change
To take advantage of the SDRAM burst read feature, the data can be loaded so that all 16 pixels of a tile
line can be read sequentially: () are 16-bit words, [] is the 4-word burst read
[(40 41) (00 01) (42 43) (02 03)]
[(44 45) (04 05) (46 47) (06 07)]...
Word interleaving is done on the FPGA side to mix the two C ROMs data (even/odd)
In: FEDCBA9876 54321 0
Out: FEDCBA9876 15432 0
*/
for (unsigned int i = 0; i < size; i++)
buf_out[i] = buf_in[(i & 0xFFC0) | (i & 1) | ((i >> 1) & 0x1E) | (((i & 2) ^ 2) << 4)];
/*
0 <- 20
1 <- 21
2 <- 00
3 <- 01
4 <- 22
5 <- 23
6 <- 02
7 <- 03
...
00 -> 02
01 -> 03
02 -> 06
03 -> 07
...
*/
}
void fix_convert(uint8_t* buf_in, uint8_t* buf_out, unsigned int size)
{
/*
In S ROMs, a byte provides two pixels
For one fix tile, bytes are used like this: ([...] represents a pair of pixels)
[10][18][00][08]
[11][19][01][09]
[12][1A][02][0A]
[13][1B][03][0B]
[14][1C][04][0C]
[15][1D][05][0D]
[16][1E][06][0E]
[17][1F][07][0F]
The data read for a given tile line (8 pixels) is always the same
To take advantage of the SDRAM burst read feature, the data can be loaded so that all 8 pixels of a tile
line can be read sequentially: () are 16-bit words, [] is the 2-word burst read
[(10 18) (00 08)]
[(11 19) (01 09)]...
In: FEDCBA9876543210
Out: FEDCBA9876510432
*/
for (unsigned int i = 0; i < size; i++)
buf_out[i] = buf_in[(i & 0xFFE0) | ((i >> 2) & 7) | ((i & 1) << 3) | (((i & 2) << 3) ^ 0x10)];
}

View File

@@ -0,0 +1,4 @@
#include "../../file_io.h"
void spr_convert(uint8_t* buf_in, uint8_t* buf_out, unsigned int size);
void fix_convert(uint8_t* buf_in, uint8_t* buf_out, unsigned int size);

361
support/neogeo/loader.cpp Normal file
View File

@@ -0,0 +1,361 @@
// Part of Neogeo_MiSTer
// (C) 2019 Sean 'furrtek' Gonsalves
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h> // clock_gettime, CLOCK_REALTIME
#include "graphics.h"
#include "loader.h"
#include "../../sxmlc.h"
#include "../../user_io.h"
#include "../../osd.h"
bool checked_ok;
void neogeo_osd_progress(const char* name, unsigned int progress) {
char progress_buf[30 + 1];
// OSD width - width of white bar on the left - max width of file name = 32 - 2 - 11 - 1 = 18
progress = (progress * 18) >> 8;
if (progress >= 18) progress = 18;
// ##############################
// NNNNNNNNNNN-PPPPPPPPPPPPPPPPPP
memset(progress_buf, ' ', 30);
memcpy(progress_buf, name, strlen(name));
for (unsigned int i = 0; i < progress; i++)
progress_buf[12 + i] = '#';
progress_buf[30] = 0;
OsdWrite(OsdGetSize() - 1, progress_buf, 0);
}
int neogeo_file_tx(const char* romset, const char* name, unsigned char neo_file_type, unsigned char index, unsigned long offset, unsigned long size)
{
fileTYPE f = {};
uint8_t buf[4096]; // Same in user_io_file_tx
uint8_t buf_out[4096];
static char name_buf[256];
unsigned long bytes2send = size;
struct timespec ts1, ts2; // DEBUG PROFILING
long us_acc = 0; // DEBUG PROFILING
if (!bytes2send) return 0;
strcpy(name_buf, "/media/fat/NeoGeo/");
if (strlen(romset)) {
strcat(name_buf, romset);
strcat(name_buf, "/");
}
strcat(name_buf, name);
if (!FileOpen(&f, name_buf, 0)) return 0;
FileSeek(&f, offset, SEEK_SET);
printf("Loading %s (offset %lu, size %lu, type %u) with index 0x%02X\n", name, offset, bytes2send, neo_file_type, index);
// Put pairs of bitplanes in the correct order for the core
if (neo_file_type == NEO_FILE_SPR) index ^= 1;
// set index byte
user_io_set_index(index);
// prepare transmission of new file
EnableFpga();
spi8(UIO_FILE_TX);
spi8(0xff);
DisableFpga();
while (bytes2send)
{
uint16_t chunk = (bytes2send > sizeof(buf)) ? sizeof(buf) : bytes2send;
FileReadAdv(&f, buf, chunk);
EnableFpga();
spi8(UIO_FILE_TX_DAT);
if (neo_file_type == NEO_FILE_RAW) {
spi_write(buf, chunk, 1);
} else if (neo_file_type == NEO_FILE_8BIT) {
spi_write(buf, chunk, 0);
} else {
if (neo_file_type == NEO_FILE_FIX)
fix_convert(buf, buf_out, sizeof(buf_out));
else if (neo_file_type == NEO_FILE_SPR)
spr_convert(buf, buf_out, sizeof(buf_out));
clock_gettime(CLOCK_REALTIME, &ts1); // DEBUG PROFILING
spi_write(buf_out, chunk, 1);
clock_gettime(CLOCK_REALTIME, &ts2); // DEBUG PROFILING
if (ts2.tv_nsec < ts1.tv_nsec) { // DEBUG PROFILING
ts2.tv_nsec += 1000000000;
ts2.tv_sec--;
}
us_acc += ((ts2.tv_nsec - ts1.tv_nsec) / 1000); // DEBUG PROFILING
}
DisableFpga();
neogeo_osd_progress(name, 256 - ((bytes2send << 8) / size));
bytes2send -= chunk;
}
// DEBUG PROFILING
printf("Gfx spi_write us total: %09ld\n", us_acc);
// mslug all C ROMs:
// spr_convert: 37680*4 = 150720us = 0.150s
// spi_write: 2300766*4 = 9203064us = 9.2s !
FileClose(&f);
// signal end of transmission
EnableFpga();
spi8(UIO_FILE_TX);
spi8(0x00);
DisableFpga();
return 1;
}
static int xml_check_files(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd)
{
const char* romset = (const char*)sd->user;
static int in_correct_romset = 0;
static char full_path[256];
switch (evt)
{
case XML_EVENT_START_NODE:
if (!strcasecmp(node->tag, "romset")) {
if (!strcasecmp(node->attributes[0].value, romset)) {
printf("Romset %s found !\n", romset);
in_correct_romset = 1;
}
else {
in_correct_romset = 0;
}
}
if (in_correct_romset) {
if (!strcasecmp(node->tag, "file")) {
for (int i = 0; i < node->n_attributes; i++) {
if (!strcasecmp(node->attributes[i].name, "name")) {
struct stat64 st;
sprintf(full_path, "%s/neogeo/%s/%s", getRootDir(), romset, node->attributes[i].value);
if (!stat64(full_path, &st)) {
printf("Found %s\n", full_path);
break;
}
else {
printf("Missing %s\n", full_path);
sprintf(full_path, "Missing %s !", node->attributes[i].value);
OsdWrite(OsdGetSize() - 1, full_path, 0);
return false;
}
}
}
}
}
break;
case XML_EVENT_END_NODE:
if (in_correct_romset) {
if (!strcasecmp(node->tag, "romset")) {
checked_ok = true;
return false;
}
}
if (!strcasecmp(node->tag, "romsets")) {
printf("Couldn't find romset %s\n", romset);
return false;
}
break;
case XML_EVENT_ERROR:
printf("XML parse: %s: ERROR %d\n", text, n);
break;
default:
break;
}
return true;
}
static int xml_load_files(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd)
{
const char* romset = (const char*)sd->user;
static char file_name[16 + 1] { "" };
static int in_correct_romset = 0;
static int in_file = 0;
static unsigned char file_index = 0;
static char file_type = 0;
static unsigned long int file_offset = 0, file_size = 0;
static unsigned char hw_type = 0, use_pcm = 0;
switch (evt)
{
case XML_EVENT_START_NODE:
if (!strcasecmp(node->tag, "romset")) {
for (int i = 0; i < node->n_attributes; i++) {
if (!strcasecmp(node->attributes[i].name, "name")) {
if (!strcasecmp(node->attributes[i].value, romset)) {
printf("Romset %s found !\n", romset);
in_correct_romset = 1;
} else {
in_correct_romset = 0;
}
} else if (!strcasecmp(node->attributes[i].name, "hw")) {
hw_type = atoi(node->attributes[i].value);
} else if (!strcasecmp(node->attributes[i].name, "pcm")) {
use_pcm = atoi(node->attributes[i].value);
}
}
}
if (in_correct_romset) {
if (!strcasecmp(node->tag, "file")) {
for (int i = 0; i < node->n_attributes; i++) {
if (!strcasecmp(node->attributes[i].name, "name"))
strncpy(file_name, node->attributes[i].value, 16);
if (!strcasecmp(node->attributes[i].name, "type")) {
file_type = *node->attributes[i].value;
if (file_type == 'S')
file_type = NEO_FILE_FIX;
else if (file_type == 'C')
file_type = NEO_FILE_SPR;
else if (file_type == 'M')
file_type = NEO_FILE_8BIT;
else
file_type = NEO_FILE_RAW;
}
if (!strcasecmp(node->attributes[i].name, "index"))
file_index = atoi(node->attributes[i].value);
if (!strcasecmp(node->attributes[i].name, "offset"))
file_offset = strtol(node->attributes[i].value, NULL, 0);
if (!strcasecmp(node->attributes[i].name, "size"))
file_size = strtol(node->attributes[i].value, NULL, 0);
}
in_file = 1;
}
}
break;
case XML_EVENT_END_NODE:
if (in_correct_romset) {
if (!strcasecmp(node->tag, "romset")) {
printf("Setting cart hardware type to %u\n", hw_type);
user_io_8bit_set_status(((uint32_t)hw_type & 3) << 24, 0x03000000);
printf("Setting cart to%s use the PCM chip\n", use_pcm ? "" : " not");
user_io_8bit_set_status(((uint32_t)use_pcm & 1) << 26, 0x04000000);
return 0;
} else if (!strcasecmp(node->tag, "file")) {
if (in_file)
neogeo_file_tx(romset, file_name, file_type, file_index, file_offset, file_size);
in_file = 0;
}
}
break;
case XML_EVENT_ERROR:
printf("XML parse: %s: ERROR %d\n", text, n);
break;
default:
break;
}
return true;
}
int neogeo_romset_tx(char* name) {
char romset[8 + 1];
int system_type;
static char full_path[1024];
memset(romset, 0, sizeof(romset));
system_type = (user_io_8bit_set_status(0, 0) >> 1) & 3;
printf("System type: %u\n", system_type);
user_io_8bit_set_status(1, 1); // Maintain reset
// Look for the romset's file list in romsets.xml
if (!(system_type & 2)) {
// Get romset name from path (which should point to a .p1 or .ep1 file)
char *p = strrchr(name, '/');
if (!p) return 0;
*p = 0;
p = strrchr(name, '/');
if (!p) return 0;
strncpy(romset, p + 1, strlen(p + 1));
sprintf(full_path, "%s/neogeo/romsets.xml", getRootDir());
SAX_Callbacks sax;
SAX_Callbacks_init(&sax);
checked_ok = false;
sax.all_event = xml_check_files;
XMLDoc_parse_file_SAX(full_path, &sax, romset);
if (!checked_ok) return 0;
sax.all_event = xml_load_files;
XMLDoc_parse_file_SAX(full_path, &sax, romset);
}
// Load system ROMs
if (strcmp(romset, "debug")) {
// Not loading the special 'debug' romset
struct stat64 st;
if (!(system_type & 2)) {
sprintf(full_path, "%s/neogeo/uni-bios.rom", getRootDir());
if (!stat64(full_path, &st)) {
// Autoload Unibios for cart systems if present
neogeo_file_tx("", "uni-bios.rom", NEO_FILE_RAW, 0, 0, 0x20000);
} else {
// Otherwise load normal system roms
if (system_type == 0)
neogeo_file_tx("", "neo-epo.sp1", NEO_FILE_RAW, 0, 0, 0x20000);
else
neogeo_file_tx("", "sp-s2.sp1", NEO_FILE_RAW, 0, 0, 0x20000);
}
} else if (system_type == 2) {
// NeoGeo CD
neogeo_file_tx("", "top-sp1.bin", NEO_FILE_RAW, 0, 0, 0x80000);
} else {
// NeoGeo CDZ
neogeo_file_tx("", "neocd.bin", NEO_FILE_RAW, 0, 0, 0x80000);
}
}
if (!(system_type & 2))
neogeo_file_tx("", "sfix.sfix", NEO_FILE_FIX, 2, 0, 0x10000);
neogeo_file_tx("", "000-lo.lo", NEO_FILE_8BIT, 1, 0, 0x10000);
if (!strcmp(romset, "kof95")) {
printf("Enabled sprite gfx gap hack for kof95\n");
user_io_8bit_set_status(0x10000000, 0x30000000);
} else if (!strcmp(romset, "whp")) {
printf("Enabled sprite gfx gap hack for whp\n");
user_io_8bit_set_status(0x20000000, 0x30000000);
} else if (!strcmp(romset, "kizuna")) {
printf("Enabled sprite gfx gap hack for kizuna\n");
user_io_8bit_set_status(0x30000000, 0x30000000);
} else
user_io_8bit_set_status(0x00000000, 0x30000000);
if (!(system_type & 2))
FileGenerateSavePath(name, (char*)full_path);
else
FileGenerateSavePath("ngcd", (char*)full_path);
user_io_file_mount((char*)full_path, 2, 1);
user_io_8bit_set_status(0, 1); // Release reset
return 1;
}

9
support/neogeo/loader.h Normal file
View File

@@ -0,0 +1,9 @@
#include "../../file_io.h"
#define NEO_FILE_RAW 0
#define NEO_FILE_8BIT 1
#define NEO_FILE_FIX 2
#define NEO_FILE_SPR 3
extern bool checked_ok;
int neogeo_romset_tx(char* name);

View File

@@ -69,6 +69,63 @@ static bool caps_status = 0;
static bool num_status = 0;
static bool scrl_status = 0;
typedef struct
{
bool track_active;
bool pregap_present;
uint8_t pre_m; // Actual "PREGAP".
uint8_t pre_s;
uint8_t pre_f;
bool ind0_present;
uint8_t ind0_m; // "Pregap" INDEX 00
uint8_t ind0_s;
uint8_t ind0_f;
uint8_t ind1_m; // "Track Start" INDEX 01
uint8_t ind1_s;
uint8_t ind1_f;
uint8_t type; // 0==AUDIO. 4==DATA.
int bytes_per_sec;
} cd_track_t;
// Track 1-99, so entry zero is unused / ignored.
cd_track_t cd_trackinfo[100];
uint8_t cd_first_track;
uint8_t cd_last_track;
static inline uint32_t msf_to_lba(uint8_t m, uint8_t s, uint8_t f)
{
return (m*60*75) + (s*75) + f;
}
uint32_t dec_2_bcd(uint32_t a)
{
uint32_t result = 0;
int shift = 0;
while (a != 0)
{
result |= (a % 10) << shift;
a /= 10;
shift += 4;
}
return result;
}
uint32_t bcd_2_dec(uint32_t a)
{
uint32_t result = 0;
uint32_t scale = 1;
while (a != 0)
{
result += (a & 0x0f) * scale;
a >>= 4;
scale *= 10;
}
return result;
}
static char last_filename[1024] = {};
void user_io_store_filename(char *filename)
{
@@ -189,14 +246,25 @@ char is_snes_core()
return (is_snes_type == 1);
}
static int is_cpc_type = 0;
char is_cpc_core()
{
return !strcasecmp(core_name, "amstrad");
if (!is_cpc_type) is_cpc_type = strcasecmp(core_name, "amstrad") ? 2 : 1;
return (is_cpc_type == 1);
}
static int is_zx81_type = 0;
char is_zx81_core()
{
return !strcasecmp(core_name, "zx81");
if (!is_zx81_type) is_zx81_type = strcasecmp(core_name, "zx81") ? 2 : 1;
return (is_zx81_type == 1);
}
static int is_neogeo_type = 0;
char is_neogeo_core()
{
if (!is_neogeo_type) is_neogeo_type = strcasecmp(core_name, "neogeo") ? 2 : 1;
return (is_neogeo_type == 1);
}
static int is_no_type = 0;
@@ -215,6 +283,9 @@ static void user_io_read_core_name()
is_x86_type = 0;
is_no_type = 0;
is_snes_type = 0;
is_cpc_type = 0;
is_zx81_type = 0;
is_neogeo_type = 0;
core_name[0] = 0;
// get core name
@@ -822,10 +893,11 @@ void user_io_sd_set_config(void)
}
// read 8+32 bit sd card status word from FPGA
uint16_t user_io_sd_get_status(uint32_t *lba)
uint16_t user_io_sd_get_status(uint32_t *lba, uint16_t *req_type)
{
uint32_t s;
uint16_t c;
uint16_t req = 0;
spi_uio_cmd_cont(UIO_GET_SDSTAT);
if (io_ver)
@@ -833,6 +905,7 @@ uint16_t user_io_sd_get_status(uint32_t *lba)
c = spi_w(0);
s = spi_w(0);
s = (s & 0xFFFF) | (((uint32_t)spi_w(0))<<16);
req = spi_w(0);
}
else
{
@@ -842,12 +915,16 @@ uint16_t user_io_sd_get_status(uint32_t *lba)
s = (s << 8) | spi_in();
s = (s << 8) | spi_in();
s = (s << 8) | spi_in();
req = spi_in();
}
DisableIO();
if (lba)
*lba = s;
if (req)
*req_type = req;
return c;
}
@@ -1609,6 +1686,225 @@ static int coldreset_req = 0;
static uint32_t res_timer = 0;
uint8_t cd_lba_to_track(uint32_t req_lba) {
uint8_t track=1;
for (track=1; track<=cd_last_track; track++) {
uint32_t toc_lba = msf_to_lba(cd_trackinfo[track].ind1_m, cd_trackinfo[track].ind1_s, cd_trackinfo[track].ind1_f); // Convert track MSF to LBA.
//printf("TOC Track LBA: %08d\n", toc_lba);
if (req_lba > toc_lba) continue; // See if the TOC LBA is > the requested LBA.
else break;
}
return track-1; // The start LBA of the PREVIOUS track checked was lower than our requested LBA.
}
int cue_pt = 0;
char cue_getch()
{
static uint8_t buf[512];
if (!(cue_pt & 0x1ff)) FileReadSec(&sd_image[1], buf);
if (cue_pt >= sd_image[1].size) return 0;
return buf[(cue_pt++) & 0x1ff];
}
char cue_readline(char *buffer)
{
char my_char = 0;
bool ret = 0;
int char_count = 0;
for (int i=0; i<1024; i++) {
ret = ( my_char = cue_getch() );
if (my_char!=0x20) { // Ditch the spaces.
buffer[char_count] = my_char;
if (ret==0 || my_char==0x0A) {
buffer[char_count+1] = 0x00; // Null terminator.
break;
}
else char_count++;
}
}
return ret;
}
void parse_cue_file(void)
{
int i_num, i_min, i_sec, i_frame, bytes_per_sec = 0;
// Clear the trackinfo before starting.
for (int i=0;i<=99;i++)
{
cd_trackinfo[i].track_active = 0;
cd_trackinfo[i].pregap_present = 0;
cd_trackinfo[i].pre_m = 0;
cd_trackinfo[i].pre_s = 0;
cd_trackinfo[i].pre_f = 0;
cd_trackinfo[i].ind0_present = 0;
cd_trackinfo[i].ind0_m = 0;
cd_trackinfo[i].ind0_s = 0;
cd_trackinfo[i].ind0_f = 0;
cd_trackinfo[i].ind1_m = 0;
cd_trackinfo[i].ind1_s = 0;
cd_trackinfo[i].ind1_f = 0;
cd_trackinfo[i].type = 0;
cd_trackinfo[i].bytes_per_sec = 0;
}
size_t i_tracks = 0;
char str[1024];
char type[5];
cue_pt = 0; // Set cue file index to zero.
int track_num = 0;
bool first_track_done = 0;
// Note: strncmp==0 means a MATCH! Because reasons.
while( i_tracks < 99 )
{
if ( !cue_readline(str) ) break; // Read in a whole line from the CUE file (until the end of the file).
if ( strncmp(str, "TRACK", 5)==0 ) { // Is this a track?
sscanf( str, "%*5s%2u%5s%*1s%4u", &track_num, type, &bytes_per_sec);
if (!first_track_done) {
first_track_done = 1;
cd_first_track = track_num;
}
}
if ( strncmp(str, "PREGAP", 6)==0 )
{
sscanf( str, "%*6s%2u:%2u:%2u", &i_min, &i_sec, &i_frame );
cd_trackinfo[track_num].pregap_present = 1;
cd_trackinfo[track_num].pre_m = i_min;
cd_trackinfo[track_num].pre_s = i_sec;
cd_trackinfo[track_num].pre_f = i_frame;
}
if ( strncmp(str, "INDEX", 5)==0 ) // Is this an Index?
{
sscanf( str, "%*5s%2u%2u:%2u:%2u", &i_num, &i_min, &i_sec, &i_frame );
cd_trackinfo[track_num].track_active = 1;
if ( strcmp(type, "AUDIO")==0 ) {
cd_trackinfo[track_num].type = 0;
bytes_per_sec = 2352; // Audio tracks assume 2352 bytes per sector, so it's not listed in the CUE file.
}
else if ( strcmp(type, "MODE1")==0 ) {
cd_trackinfo[track_num].type = 4;
}
cd_trackinfo[track_num].bytes_per_sec = bytes_per_sec;
/*
if (i_num==0) { // "Pregap" index, sort of.
printf("Track:%02d Pregap:%d M:%02d S:%02d F:%02d Type:%s TOCtype:%d BPS:%04d\n", track_num, cd_trackinfo[track_num].pregap_present, i_min, i_sec, i_frame, type, cd_trackinfo[track_num].type, bytes_per_sec);
cd_trackinfo[track_num].ind0_m = i_min;
cd_trackinfo[track_num].ind0_s = i_sec;
cd_trackinfo[track_num].ind0_f = i_frame;
}
*/
if (i_num==1) { // "Track Start" index.
printf("Track:%02d Pregap:%d M:%02d S:%02d F:%02d Type:%s TOCtype:%d BPS:%04d\n", track_num, cd_trackinfo[track_num].pregap_present, i_min, i_sec, i_frame, type, cd_trackinfo[track_num].type, bytes_per_sec);
cd_trackinfo[track_num].ind1_m = i_min;
cd_trackinfo[track_num].ind1_s = i_sec;
cd_trackinfo[track_num].ind1_f = i_frame;
}
}
i_tracks++;
}
cd_last_track = track_num;
}
void cd_generate_toc(uint16_t req_type, uint8_t *buffer)
{
uint8_t m,s,f;
uint32_t lba;
switch ( (req_type&0xFF00)>>8 ) {
case 0xD0: { // Request First Track and Last Track (BCD).
//buffer[0] = 0x01; // Rondo - First track (BCD).
//buffer[1] = 0x22; // Rondo - Last track (BCD).
buffer[0] = dec_2_bcd( cd_first_track );
buffer[1] = dec_2_bcd( cd_last_track );
buffer[2] = 0x00; // Padding.
buffer[3] = 0x00; // Padding.
printf("Core requesting CD TOC0. First Track:%02X. Last Track:%02X (BCD)\n", buffer[0], buffer[1]);
}; break;
case 0xD1: { // Request Total Disk Size (MSF, in BCD).
//buffer[0] = 0x49; // Rondo - Minutes = 0x49 (73).
//buffer[1] = 0x09; // Rondo - Seconds = 0x09 (9).
//buffer[2] = 0x12; // Rondo - Frames = 0x12 (18).
// ADD the PREGAP (if present).
/*
if (buffer[3]==4 && cd_trackinfo[track].pregap_present) {
m = cd_trackinfo[cd_last_track].ind1_m + cd_trackinfo[cd_last_track].pre_m;
s = cd_trackinfo[cd_last_track].ind1_s + cd_trackinfo[cd_last_track].pre_s;
f = cd_trackinfo[cd_last_track].ind1_f + cd_trackinfo[cd_last_track].pre_f;
// Not sure if audio tracks need the 2-second lead-in offset added? ElectronAsh.
uint32_t lba = msf_to_lba(m, s, f); // Convert to LBA, so we can add the 2-second lead-in.
//lba += 2*75; // Standard lead-in is 2 seconds (75 sectors per second, so 150).
// Convert back from LBA to MSF...
m = lba / (60 * 75);
lba -= m * (60 * 75);
s = lba / 75;
f = lba % 75;
buffer[0] = dec_2_bcd( m );
buffer[1] = dec_2_bcd( s );
buffer[2] = dec_2_bcd( f );
}
else
{*/
buffer[0] = dec_2_bcd( cd_trackinfo[cd_last_track].ind1_m );
buffer[1] = dec_2_bcd( cd_trackinfo[cd_last_track].ind1_s );
buffer[2] = dec_2_bcd( cd_trackinfo[cd_last_track].ind1_f );
//}
buffer[3] = 0x00; // Padding.
printf("Core requesting CD TOC1. Total Disk Size:M:%02X S:%02X F:%02X (BCD)\n", buffer[0], buffer[1], buffer[2]);
}; break;
case 0xD2: { // Request Track Info (Start MSF in BCD, and track type).
uint8_t track = bcd_2_dec(req_type&0xFF); // Track number from req_type upper byte is in BCD!
// If a DATA track, check for a pregap, and ADD it (if present).
if (cd_trackinfo[track].type==4 && cd_trackinfo[track].pregap_present) {
m = cd_trackinfo[track].ind1_m + cd_trackinfo[track].pre_m;
s = cd_trackinfo[track].ind1_s + cd_trackinfo[track].pre_s;
f = cd_trackinfo[track].ind1_f + cd_trackinfo[track].pre_f;
lba = msf_to_lba(m, s, f); // Convert to LBA, so we can add the 2-second lead-in.
lba += 2*75; // Standard lead-in is 2 seconds (75 sectors per second, so 150).
// Convert back from LBA to MSF...
m = lba / (60 * 75);
lba -= m * (60 * 75);
s = lba / 75;
f = lba % 75;
buffer[0] = dec_2_bcd( m );
buffer[1] = dec_2_bcd( s );
buffer[2] = dec_2_bcd( f );
}
else
{
buffer[0] = dec_2_bcd( cd_trackinfo[track].ind1_m );
buffer[1] = dec_2_bcd( cd_trackinfo[track].ind1_s );
buffer[2] = dec_2_bcd( cd_trackinfo[track].ind1_f );
}
buffer[3] = cd_trackinfo[track].type;
printf("Core requesting CD TOC2. Track:%02d. M:%02X S:%02X F:%02X (BCD). Type:", track, buffer[0], buffer[1], buffer[2]);
if (buffer[3]==0x00) printf("AUDIO\n");
else if (buffer[3]==0x04) printf("DATA\n");
else printf("UNKNOWN!\n");
}; break;
}
}
void user_io_poll()
{
if ((core_type != CORE_TYPE_MINIMIG2) &&
@@ -1772,7 +2068,8 @@ void user_io_poll()
{
static uint8_t buffer[4][512];
uint32_t lba;
uint16_t c = user_io_sd_get_status(&lba);
uint16_t req_type = 0;
uint16_t c = user_io_sd_get_status(&lba, &req_type);
//if(c&3) printf("user_io_sd_get_status: cmd=%02x, lba=%08x\n", c, lba);
// valid sd commands start with "5x" to avoid problems with
@@ -1861,18 +2158,111 @@ void user_io_poll()
//printf("SD RD %d on %d, WIDE=%d\n", lba, disk, fio_size);
int done = 0;
if (buffer_lba[disk] != lba)
if (is_neogeo_core())
{
uint32_t offset = 0;
if (sd_image[disk].size)
{
diskled_on();
if (FileSeekLBA(&sd_image[disk], lba))
printf("req_type: 0x%04X ", req_type);
switch ((req_type & 0xFF00) >> 8)
{
if (FileReadSec(&sd_image[disk], buffer[disk]))
case 0xD0:case 0xD1:case 0xD2:
{
cd_generate_toc(req_type, buffer[disk]);
done = 1;
};
break;
case 0x48:
{
// Added this, Neo CD always requests by MSF (furrtek)
if ((req_type & 0xFF) == 0x01)
{
done = 1;
printf("Neo CD requested raw lba value (MSF): 0x%08X\n", lba);
uint8_t m = bcd_2_dec((lba & 0xFF0000) >> 16);
uint8_t s = bcd_2_dec((lba & 0xFF00) >> 8);
uint8_t f = bcd_2_dec((lba & 0xFF) >> 0);
lba = msf_to_lba(m, s, f);
lba -= (2 * 75); // Remove 2 second pregap
}
uint8_t track = cd_lba_to_track(lba);
uint16_t bps = cd_trackinfo[track].bytes_per_sec;
uint32_t pregap = 0;
if (cd_trackinfo[track].pregap_present)
{
pregap = msf_to_lba(cd_trackinfo[track].pre_m, cd_trackinfo[track].pre_s, cd_trackinfo[track].pre_f);
}
if (bps == 2352) offset = 16 + ((lba - pregap) * 2352); // Rondo etc.
else if (bps == 2048) offset = ((lba - pregap) * 2048); // Homebrew, etc.
else printf("Data track %02d has unhandled bytes-per-sec of %d !\n", track, bps);
if (FileSeek(&sd_image[disk], offset, SEEK_SET))
{
if (FileReadAdv(&sd_image[disk], buffer[disk], 2048)) done = 1;
}
printf("Core requesting 2048-byte CD sector, from LBA: 0x%08X TRACK: %02d BPS: %04d OFFSET: 0x%08X \n", lba, track, bps, offset);
};
break;
case 0x52:
{
switch (req_type & 0xFF)
{
// "lba" holds the LBA. Dun do nothing. (no conversion needed).
case 0x00:
break;
// "lba" holds the MSF (BCD). Convert to LBA.
case 0x01:
{
uint8_t m = bcd_2_dec((lba & 0xFF0000) >> 16);
uint8_t s = bcd_2_dec((lba & 0xFF00) >> 8);
uint8_t f = bcd_2_dec((lba & 0xFF) >> 0);
lba = msf_to_lba(m, s, f);
};
break;
// "lba" holds the TRACK number (BCD?). Grab the track start MSF from the TOC, then convert to LBA.
case 0x02:
{
uint8_t track = bcd_2_dec(lba);
lba = msf_to_lba(cd_trackinfo[track].ind1_m, cd_trackinfo[track].ind1_s, cd_trackinfo[track].ind1_f);
};
break;
}
uint8_t track = cd_lba_to_track(lba);
if (cd_trackinfo[track].type != 0x00)
{
printf("Error: Core is trying to play back non-audio track as CDDA!\n");
memset(buffer[disk], 0, sizeof(buffer[disk]));
}
else
{
if (FileSeek(&sd_image[disk], (lba - 525) * 2352, SEEK_SET))
{
if (FileReadAdv(&sd_image[disk], buffer[disk], 2352)) done = 1;
}
}
printf("Core requesting a raw 2352-byte CD sector, from LBA: 0x%08X TRACK: %02d\n", lba, track);
};
break;
default:
{
if (FileSeekLBA(&sd_image[disk], lba))
{
if (FileReadSec(&sd_image[disk], buffer[disk])) done = 1;
}
printf("Core requesting a 512-byte SD / VHD sector, from LBA: 0x%08X\n", lba);
};
break;
}
}
@@ -1880,37 +2270,66 @@ void user_io_poll()
//Give an empty block.
if (!done) memset(buffer[disk], 0, sizeof(buffer[disk]));
buffer_lba[disk] = lba;
}
if(buffer_lba[disk] == lba)
{
//hexdump(buffer, 32, 0);
// data is now stored in buffer. send it to fpga
spi_uio_cmd_cont(UIO_SECTOR_RD);
spi_block_write(buffer[disk], fio_size);
if ((req_type & 0xF000) == 0xD000) spi_write(buffer[disk], 4, fio_size); // TOC. (4 bytes, including padding).
else if ((req_type & 0xFF00) == 0x4800) spi_write(buffer[disk], 2048, fio_size); // 2048-byte CD sector.
else if ((req_type & 0xFF00) == 0x5200) spi_write(buffer[disk], 2352, fio_size); // 2352-byte CD sector.
else spi_write(buffer[disk], 512, fio_size); // Standard 512-byte SD / VHD sector.
DisableIO();
}
// just load the next sector now, so it may be prefetched
// for the next request already
done = 0;
if (sd_image[disk].size)
else
{
diskled_on();
if (FileSeekLBA(&sd_image[disk], lba + 1))
if (buffer_lba[disk] != lba)
{
if (FileReadSec(&sd_image[disk], buffer[disk]))
if (sd_image[disk].size)
{
done = 1;
diskled_on();
if (FileSeekLBA(&sd_image[disk], lba))
{
if (FileReadSec(&sd_image[disk], buffer[disk]))
{
done = 1;
}
}
}
//Even after error we have to provide the block to the core
//Give an empty block.
if (!done) memset(buffer[disk], 0, sizeof(buffer[disk]));
buffer_lba[disk] = lba;
}
if (buffer_lba[disk] == lba)
{
//hexdump(buffer, 32, 0);
// data is now stored in buffer. send it to fpga
spi_uio_cmd_cont(UIO_SECTOR_RD);
spi_block_write(buffer[disk], fio_size);
DisableIO();
}
// just load the next sector now, so it may be prefetched
// for the next request already
done = 0;
if (sd_image[disk].size)
{
diskled_on();
if (FileSeekLBA(&sd_image[disk], lba + 1))
{
if (FileReadSec(&sd_image[disk], buffer[disk]))
{
done = 1;
}
}
}
}
if(done) buffer_lba[disk] = lba + 1;
if (done) buffer_lba[disk] = lba + 1;
if (sd_image[disk].type == 2)
{
buffer_lba[disk] = -1;
if (sd_image[disk].type == 2)
{
buffer_lba[disk] = -1;
}
}
}
}
@@ -1997,6 +2416,13 @@ void user_io_poll()
}
}
if (is_neogeo_core() && (!rtc_timer || CheckTimer(rtc_timer)))
{
// Update once per minute should be enough
rtc_timer = GetTimer(60000);
send_rtc(1);
}
if (core_type == CORE_TYPE_ARCHIE) archie_poll();
if (core_type == CORE_TYPE_SHARPMZ) sharpmz_poll();

View File

@@ -182,9 +182,6 @@ typedef struct {
void user_io_init(const char *path);
unsigned char user_io_core_type();
char is_minimig();
char is_archie();
char is_sharpmz();
void user_io_poll();
char user_io_menu_button();
char user_io_user_button();
@@ -198,9 +195,6 @@ int user_io_file_mount(char *name, unsigned char index = 0, char pre = 0);
char user_io_serial_status(serial_status_t *, uint8_t);
char *user_io_get_core_name();
const char *user_io_get_core_name_ex();
char is_menu_core();
char is_x86_core();
char is_snes_core();
char has_menu();
const char *get_image_name(int i);
@@ -236,8 +230,6 @@ void user_io_rtc_reset();
const char* get_rbf_dir();
const char* get_rbf_name();
#define HomeDir (is_minimig() ? "Amiga" : is_archie() ? "Archie" : is_menu_core() ? "Scripts" : user_io_get_core_name())
int GetUARTMode();
int GetMidiLinkMode();
void SetMidiLinkMode(int mode);
@@ -252,4 +244,16 @@ void diskled_on();
#define DISKLED_ON diskled_on()
#define DISKLED_OFF void()
void parse_cue_file(void);
char is_minimig();
char is_archie();
char is_sharpmz();
char is_menu_core();
char is_x86_core();
char is_snes_core();
char is_neogeo_core();
#define HomeDir (is_minimig() ? "Amiga" : is_archie() ? "Archie" : is_menu_core() ? "Scripts" : user_io_get_core_name())
#endif // USER_IO_H