mirror of
https://github.com/libretro/beetle-lynx-libretro.git
synced 2024-11-23 08:10:55 +00:00
Update PSX core to 0.9.31-wip.
This commit is contained in:
parent
67a2d23ef4
commit
377a85b857
89
Makefile
89
Makefile
@ -29,11 +29,12 @@ ifeq ($(core), psx)
|
||||
core = psx
|
||||
PTHREAD_FLAGS = -pthread
|
||||
NEED_CD = 1
|
||||
NEED_TREMOR = 1
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_DEINTERLACER = 1
|
||||
NEED_THREADING = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_THREADING = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
CORE_DEFINE := -DWANT_PSX_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/psx
|
||||
CORE_SOURCES := $(CORE_DIR)/psx.cpp \
|
||||
@ -65,9 +66,9 @@ else ifeq ($(core), pce-fast)
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_CD = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_SCSI_CD = 1
|
||||
NEED_THREADING = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_SCSI_CD = 1
|
||||
NEED_THREADING = 1
|
||||
NEED_CRC32 = 1
|
||||
CORE_DEFINE := -DWANT_PCE_FAST_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/pce_fast
|
||||
@ -90,7 +91,7 @@ else ifeq ($(core), wswan)
|
||||
core = wswan
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
CORE_DEFINE := -DWANT_WSWAN_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/wswan
|
||||
|
||||
@ -108,7 +109,7 @@ else ifeq ($(core), ngp)
|
||||
core = ngp
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
CORE_DEFINE := -DWANT_NGP_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/ngp
|
||||
|
||||
@ -146,7 +147,7 @@ else ifeq ($(core), gba)
|
||||
core = gba
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_CRC32 = 1
|
||||
CORE_DEFINE := -DWANT_GBA_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/gba
|
||||
@ -179,7 +180,7 @@ else ifeq ($(core), vb)
|
||||
core = vb
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
CORE_DEFINE := -DWANT_VB_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/vb
|
||||
|
||||
@ -198,10 +199,10 @@ else ifeq ($(core), pcfx)
|
||||
core = pcfx
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_CD = 1
|
||||
NEED_SCSI_CD = 1
|
||||
NEED_THREADING = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_CD = 1
|
||||
NEED_SCSI_CD = 1
|
||||
NEED_THREADING = 1
|
||||
CORE_DEFINE := -DWANT_PCFX_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/pcfx
|
||||
|
||||
@ -229,7 +230,7 @@ else ifeq ($(core), snes)
|
||||
core = snes
|
||||
NEED_BPP = 32
|
||||
NEED_BLIP = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
NEED_STEREO_SOUND = 1
|
||||
CORE_DEFINE := -DWANT_SNES_EMU
|
||||
CORE_DIR := $(MEDNAFEN_DIR)/snes
|
||||
|
||||
@ -329,11 +330,11 @@ else ifeq ($(platform), qnx)
|
||||
ENDIANNESS_DEFINES := -DLSB_FIRST
|
||||
#LDFLAGS += $(PTHREAD_FLAGS)
|
||||
#FLAGS += $(PTHREAD_FLAGS) -DHAVE_MKDIR
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
CC = qcc -Vgcc_ntoarmv7le
|
||||
CXX = QCC -Vgcc_ntoarmv7le_cpp
|
||||
AR = QCC -Vgcc_ntoarmv7le
|
||||
FLAGS += -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
CC = qcc -Vgcc_ntoarmv7le
|
||||
CXX = QCC -Vgcc_ntoarmv7le_cpp
|
||||
AR = QCC -Vgcc_ntoarmv7le
|
||||
FLAGS += -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
|
||||
else ifeq ($(platform), ps3)
|
||||
TARGET := $(TARGET_NAME)_ps3.a
|
||||
CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
|
||||
@ -342,7 +343,7 @@ else ifeq ($(platform), ps3)
|
||||
ENDIANNESS_DEFINES := -DMSB_FIRST -DBYTE_ORDER=BIG_ENDIAN
|
||||
OLD_GCC := 1
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), sncps3)
|
||||
TARGET := $(TARGET_NAME)_ps3.a
|
||||
CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
@ -353,7 +354,7 @@ else ifeq ($(platform), sncps3)
|
||||
OLD_GCC := 1
|
||||
NO_GCC := 1
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), psl1ght)
|
||||
TARGET := $(TARGET_NAME)_psl1ght.a
|
||||
CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT)
|
||||
@ -361,16 +362,16 @@ else ifeq ($(platform), psl1ght)
|
||||
AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT)
|
||||
ENDIANNESS_DEFINES := -DMSB_FIRST -DBYTE_ORDER=BIG_ENDIAN
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), psp1)
|
||||
TARGET := $(TARGET_NAME)_psp1.a
|
||||
CC = psp-gcc$(EXE_EXT)
|
||||
CXX = psp-g++$(EXE_EXT)
|
||||
AR = psp-ar$(EXE_EXT)
|
||||
ENDIANNESS_DEFINES := -DLSB_FIRST
|
||||
FLAGS += -DPSP -G0
|
||||
TARGET := $(TARGET_NAME)_psp1.a
|
||||
CC = psp-gcc$(EXE_EXT)
|
||||
CXX = psp-g++$(EXE_EXT)
|
||||
AR = psp-ar$(EXE_EXT)
|
||||
ENDIANNESS_DEFINES := -DLSB_FIRST
|
||||
FLAGS += -DPSP -G0
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), xenon)
|
||||
TARGET := $(TARGET_NAME)_xenon360.a
|
||||
CC = xenon-gcc$(EXE_EXT)
|
||||
@ -379,7 +380,7 @@ else ifeq ($(platform), xenon)
|
||||
ENDIANNESS_DEFINES += -D__LIBXENON__ -m32 -D__ppc__ -DMSB_FIRST -DBYTE_ORDER=BIG_ENDIAN
|
||||
LIBS := $(PTHREAD_FLAGS)
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), ngc)
|
||||
TARGET := $(TARGET_NAME)_ngc.a
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
@ -389,7 +390,7 @@ else ifeq ($(platform), ngc)
|
||||
|
||||
EXTRA_INCLUDES := -I$(DEVKITPRO)/libogc/include
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifeq ($(platform), wii)
|
||||
TARGET := $(TARGET_NAME)_wii.a
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
@ -399,7 +400,7 @@ else ifeq ($(platform), wii)
|
||||
|
||||
EXTRA_INCLUDES := -I$(DEVKITPRO)/libogc/include
|
||||
FLAGS += -DHAVE_MKDIR
|
||||
STATIC_LINKING = 1
|
||||
STATIC_LINKING = 1
|
||||
else ifneq (,$(findstring armv,$(platform)))
|
||||
TARGET := $(TARGET_NAME).so
|
||||
fpic := -fPIC
|
||||
@ -432,7 +433,7 @@ else
|
||||
TARGET := $(TARGET_NAME).dll
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
IS_X86 = 1
|
||||
IS_X86 = 1
|
||||
SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T
|
||||
LDFLAGS += -static-libgcc -static-libstdc++ -lwinmm
|
||||
ENDIANNESS_DEFINES := -DLSB_FIRST
|
||||
@ -440,24 +441,25 @@ IS_X86 = 1
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_THREADING), 1)
|
||||
FLAGS += -DWANT_THREADING
|
||||
FLAGS += -DWANT_THREADING
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_CRC32), 1)
|
||||
FLAGS += -DWANT_CRC32
|
||||
FLAGS += -DWANT_CRC32
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_DEINTERLACER), 1)
|
||||
FLAGS += -DNEED_DEINTERLACER
|
||||
FLAGS += -DNEED_DEINTERLACER
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_SCSI_CD), 1)
|
||||
CDROM_SOURCES += $(MEDNAFEN_DIR)/cdrom/scsicd.cpp
|
||||
CDROM_SOURCES += $(MEDNAFEN_DIR)/cdrom/scsicd.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_CD), 1)
|
||||
CDROM_SOURCES += $(MEDNAFEN_DIR)/cdrom/CDAccess.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/CDAccess_Image.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/CDAccess_CCD.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/CDUtility.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/lec.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/SimpleFIFO.cpp \
|
||||
@ -465,14 +467,14 @@ CDROM_SOURCES += $(MEDNAFEN_DIR)/cdrom/CDAccess.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/galois.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/recover-raw.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/l-ec.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/cdromif.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/cd_crc32.cpp
|
||||
FLAGS += -DNEED_CD
|
||||
$(MEDNAFEN_DIR)/cdrom/crc32.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/cdromif.cpp
|
||||
FLAGS += -DNEED_CD
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_TREMOR), 1)
|
||||
TREMOR_SRC := $(wildcard $(MEDNAFEN_DIR)/tremor/*.c)
|
||||
FLAGS += -DNEED_TREMOR
|
||||
TREMOR_SRC := $(wildcard $(MEDNAFEN_DIR)/tremor/*.c)
|
||||
FLAGS += -DNEED_TREMOR
|
||||
endif
|
||||
|
||||
|
||||
@ -512,6 +514,7 @@ WARNINGS := -Wall \
|
||||
-Wno-unused-variable \
|
||||
-Wno-unused-function \
|
||||
-Wno-uninitialized \
|
||||
-Wno-narrowing \
|
||||
$(NEW_GCC_WARNING_FLAGS) \
|
||||
-Wno-strict-aliasing
|
||||
|
||||
@ -537,7 +540,7 @@ LDFLAGS += $(fpic) $(SHARED)
|
||||
FLAGS += $(fpic) $(NEW_GCC_FLAGS)
|
||||
FLAGS += -I. -Imednafen -Imednafen/include -Imednafen/intl -Imednafen/hw_misc -Imednafen/hw_sound -Imednafen/hw_cpu $(CORE_INCDIR) $(EXTRA_CORE_INCDIR)
|
||||
|
||||
FLAGS += $(ENDIANNESS_DEFINES) -DSIZEOF_DOUBLE=8 $(WARNINGS) -DMEDNAFEN_VERSION=\"0.9.28\" -DPACKAGE=\"mednafen\" -DMEDNAFEN_VERSION_NUMERIC=928 -DPSS_STYLE=1 -DMPC_FIXED_POINT $(CORE_DEFINE) -DSTDC_HEADERS -D__STDC_LIMIT_MACROS -D__LIBRETRO__ -DNDEBUG -D_LOW_ACCURACY_ $(EXTRA_INCLUDES) $(SOUND_DEFINE) -Dgettext_noop\(a\)=a
|
||||
FLAGS += $(ENDIANNESS_DEFINES) -DSIZEOF_DOUBLE=8 $(WARNINGS) -DMEDNAFEN_VERSION=\"0.9.31\" -DPACKAGE=\"mednafen\" -DMEDNAFEN_VERSION_NUMERIC=931 -DPSS_STYLE=1 -DMPC_FIXED_POINT $(CORE_DEFINE) -DSTDC_HEADERS -D__STDC_LIMIT_MACROS -D__LIBRETRO__ -DNDEBUG -D_LOW_ACCURACY_ $(EXTRA_INCLUDES) $(SOUND_DEFINE) -Dgettext_noop\(a\)=a
|
||||
|
||||
ifeq ($(IS_X86), 1)
|
||||
FLAGS += -DARCH_X86
|
||||
|
21
libretro.cpp
21
libretro.cpp
@ -50,8 +50,8 @@ static Deinterlacer deint;
|
||||
#if defined(WANT_PSX_EMU)
|
||||
#define MEDNAFEN_CORE_NAME_MODULE "psx"
|
||||
#define MEDNAFEN_CORE_NAME "Mednafen PSX"
|
||||
#define MEDNAFEN_CORE_VERSION "v0.9.28"
|
||||
#define MEDNAFEN_CORE_EXTENSIONS "cue|toc|m3u"
|
||||
#define MEDNAFEN_CORE_VERSION "v0.9.31"
|
||||
#define MEDNAFEN_CORE_EXTENSIONS "cue|toc|m3u|ccd"
|
||||
#define MEDNAFEN_CORE_TIMING_FPS 59.82704 // Hardcoded for NTSC atm.
|
||||
#define MEDNAFEN_CORE_GEOMETRY_BASE_W 320
|
||||
#define MEDNAFEN_CORE_GEOMETRY_BASE_H 240
|
||||
@ -287,7 +287,7 @@ static bool disk_replace_image_index(unsigned index, const struct retro_game_inf
|
||||
|
||||
try
|
||||
{
|
||||
CDIF *iface = CDIF_Open(info->path, false);
|
||||
CDIF *iface = CDIF_Open(info->path, false, false);
|
||||
delete cdifs->at(index);
|
||||
cdifs->at(index) = iface;
|
||||
CalcDiscSCEx();
|
||||
@ -469,6 +469,8 @@ static void check_variables(void)
|
||||
fprintf(stderr, "PCE CD Audio settings changed.\n");
|
||||
}
|
||||
#elif defined(WANT_PSX_EMU)
|
||||
|
||||
#if 0
|
||||
var.key = "psx_dithering";
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
|
||||
@ -481,6 +483,7 @@ static void check_variables(void)
|
||||
|
||||
PSXDitherApply(apply_dither);
|
||||
}
|
||||
#endif
|
||||
|
||||
var.key = "psx_fastboot";
|
||||
|
||||
@ -531,8 +534,8 @@ static void check_variables(void)
|
||||
#define MAX_BUTTONS 16
|
||||
union
|
||||
{
|
||||
uint32_t u32[MAX_PLAYERS][1 + 8];
|
||||
uint8_t u8[MAX_PLAYERS][MAX_PLAYERS * sizeof(uint16_t) + 8 * sizeof(uint32_t)];
|
||||
uint32_t u32[MAX_PLAYERS][1 + 8 + 1]; // Buttons + Axes + Rumble
|
||||
uint8_t u8[MAX_PLAYERS][(1 + 8 + 1) * sizeof(uint32_t)];
|
||||
} static buf;
|
||||
static uint16_t input_buf[MAX_PLAYERS] = {0};
|
||||
|
||||
@ -659,6 +662,8 @@ bool retro_load_game(const struct retro_game_info *info)
|
||||
|
||||
set_basename(info->path);
|
||||
|
||||
check_variables();
|
||||
|
||||
game = MDFNI_LoadGame(MEDNAFEN_CORE_NAME_MODULE, info->path);
|
||||
if (!game)
|
||||
return false;
|
||||
@ -675,8 +680,6 @@ bool retro_load_game(const struct retro_game_info *info)
|
||||
|
||||
hookup_ports(true);
|
||||
|
||||
check_variables();
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
@ -1212,13 +1215,13 @@ void retro_set_controller_port_device(unsigned in_port, unsigned device)
|
||||
// TODO: Add support for other input types
|
||||
case RETRO_DEVICE_JOYPAD:
|
||||
case RETRO_DEVICE_ANALOG:
|
||||
fprintf(stderr, "[%s]: Selected controller type %u", mednafen_core_str, device);
|
||||
fprintf(stderr, "[%s]: Selected controller type %u.\n", mednafen_core_str, device);
|
||||
retro_devices[in_port] = device;
|
||||
break;
|
||||
default:
|
||||
retro_devices[in_port] = RETRO_DEVICE_JOYPAD;
|
||||
fprintf(stderr,
|
||||
"[%s]: Unsupported controller device, falling back to gamepad", mednafen_core_str);
|
||||
"[%s]: Unsupported controller device, falling back to gamepad.\n", mednafen_core_str);
|
||||
}
|
||||
|
||||
hookup_ports(true);
|
||||
|
40
mednafen/cdrom-0928/CDAccess.cpp
Normal file
40
mednafen/cdrom-0928/CDAccess.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
CDAccess::CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess::~CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open(const char *path, bool image_memcache)
|
||||
{
|
||||
return new CDAccess_Image(path, image_memcache);
|
||||
}
|
||||
|
24
mednafen/cdrom-0928/CDAccess.h
Normal file
24
mednafen/cdrom-0928/CDAccess.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef __MDFN_CDROMFILE_H
|
||||
#define __MDFN_CDROMFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "CDUtility.h"
|
||||
|
||||
class CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess();
|
||||
virtual ~CDAccess();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba) = 0;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
|
||||
|
||||
virtual void Eject(bool eject_status) = 0; // Eject a disc if it's physical, otherwise NOP. Returns true on success(or NOP), false on error
|
||||
};
|
||||
|
||||
CDAccess *cdaccess_open(const char *path, bool image_memcache);
|
||||
|
||||
#endif
|
964
mednafen/cdrom-0928/CDAccess_Image.cpp
Normal file
964
mednafen/cdrom-0928/CDAccess_Image.cpp
Normal file
@ -0,0 +1,964 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes and TODO:
|
||||
|
||||
POSTGAP in CUE sheets may not be handled properly, should the directive automatically increment the index number?
|
||||
|
||||
INDEX nn where 02 <= nn <= 99 is not supported in CUE sheets.
|
||||
|
||||
TOC reading code is extremely barebones, leaving out support for more esoteric features.
|
||||
|
||||
A PREGAP statement in the first track definition in a CUE sheet may not work properly(depends on what is proper);
|
||||
it will be added onto the implicit default 00:02:00 of pregap.
|
||||
|
||||
Trying to read sectors at an LBA of less than 0 is not supported. TODO: support it(at least up to -150).
|
||||
*/
|
||||
|
||||
#define _CDROMFILE_INTERNAL
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <trio/trio.h>
|
||||
|
||||
#include "../general.h"
|
||||
#include "../mednafen-endian.h"
|
||||
#include "../FileStream.h"
|
||||
#include "../MemoryStream.h"
|
||||
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
|
||||
#include "audioreader.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../msvc_compat.h"
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
enum
|
||||
{
|
||||
CDRF_SUBM_NONE = 0,
|
||||
CDRF_SUBM_RW = 1,
|
||||
CDRF_SUBM_RW_RAW = 2
|
||||
};
|
||||
|
||||
// Disk-image(rip) track/sector formats
|
||||
enum
|
||||
{
|
||||
DI_FORMAT_AUDIO = 0x00,
|
||||
DI_FORMAT_MODE1 = 0x01,
|
||||
DI_FORMAT_MODE1_RAW = 0x02,
|
||||
DI_FORMAT_MODE2 = 0x03,
|
||||
DI_FORMAT_MODE2_FORM1 = 0x04,
|
||||
DI_FORMAT_MODE2_FORM2 = 0x05,
|
||||
DI_FORMAT_MODE2_RAW = 0x06,
|
||||
_DI_FORMAT_COUNT
|
||||
};
|
||||
|
||||
static const int32 DI_Size_Table[7] =
|
||||
{
|
||||
2352, // Audio
|
||||
2048, // MODE1
|
||||
2352, // MODE1 RAW
|
||||
2336, // MODE2
|
||||
2048, // MODE2 Form 1
|
||||
2324, // Mode 2 Form 2
|
||||
2352
|
||||
};
|
||||
|
||||
static const char *DI_CDRDAO_Strings[7] =
|
||||
{
|
||||
"AUDIO",
|
||||
"MODE1",
|
||||
"MODE1_RAW",
|
||||
"MODE2",
|
||||
"MODE2_FORM1",
|
||||
"MODE2_FORM2",
|
||||
"MODE2_RAW"
|
||||
};
|
||||
|
||||
static const char *DI_CUE_Strings[7] =
|
||||
{
|
||||
"AUDIO",
|
||||
"MODE1/2048",
|
||||
"MODE1/2352",
|
||||
|
||||
// FIXME: These are just guesses:
|
||||
"MODE2/2336",
|
||||
"MODE2/2048",
|
||||
"MODE2/2324",
|
||||
"MODE2/2352"
|
||||
};
|
||||
|
||||
static char *UnQuotify(char *src, char *dest)
|
||||
{
|
||||
bool in_quote = 0;
|
||||
bool already_normal = 0;
|
||||
|
||||
while(*src)
|
||||
{
|
||||
if(*src == ' ' || *src == '\t')
|
||||
{
|
||||
if(!in_quote)
|
||||
{
|
||||
if(already_normal)
|
||||
break;
|
||||
else
|
||||
{
|
||||
src++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*src == '"')
|
||||
{
|
||||
if(in_quote)
|
||||
{
|
||||
src++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
in_quote = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest = *src;
|
||||
already_normal = 1;
|
||||
dest++;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
|
||||
*dest = 0;
|
||||
return(src);
|
||||
}
|
||||
|
||||
uint32 CDAccess_Image::GetSectorCount(CDRFILE_TRACK_INFO *track)
|
||||
{
|
||||
if(track->DIFormat == DI_FORMAT_AUDIO)
|
||||
{
|
||||
if(track->AReader)
|
||||
return(((track->AReader->FrameCount() * 4) - track->FileOffset) / 2352);
|
||||
else
|
||||
{
|
||||
const int64 size = track->fp->size();
|
||||
|
||||
//printf("%d %d %d\n", (int)stat_buf.st_size, (int)track->FileOffset, (int)stat_buf.st_size - (int)track->FileOffset);
|
||||
if(track->SubchannelMode)
|
||||
return((size - track->FileOffset) / (2352 + 96));
|
||||
else
|
||||
return((size - track->FileOffset) / 2352);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int64 size = track->fp->size();
|
||||
|
||||
return((size - track->FileOffset) / DI_Size_Table[track->DIFormat]);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool CDAccess_Image::ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const char *filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache)
|
||||
{
|
||||
long offset = 0; // In bytes!
|
||||
long tmp_long;
|
||||
int m, s, f;
|
||||
uint32 sector_mult;
|
||||
long sectors;
|
||||
std::string efn;
|
||||
|
||||
efn = MDFN_EvalFIP(base_dir, filename);
|
||||
|
||||
FILE *dummy = fopen(efn.c_str(), "rb");
|
||||
|
||||
// test if file exists - if not exit prematurely
|
||||
|
||||
if(dummy)
|
||||
fclose(dummy);
|
||||
else
|
||||
return false;
|
||||
|
||||
track->fp = new FileStream(efn.c_str(), FileStream::MODE_READ);
|
||||
if(MDFN_GetSettingB("libretro.cd_load_into_ram"))
|
||||
track->fp = new MemoryStream(track->fp);
|
||||
|
||||
if(strlen(filename) >= 4 && !strcasecmp(filename + strlen(filename) - 4, ".wav"))
|
||||
{
|
||||
track->AReader = AR_Open(track->fp);
|
||||
|
||||
if(!track->AReader)
|
||||
{
|
||||
fprintf(stderr, "TODO ERROR.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sector_mult = DI_Size_Table[track->DIFormat];
|
||||
|
||||
if(track->SubchannelMode)
|
||||
sector_mult += 96;
|
||||
|
||||
if(binoffset && trio_sscanf(binoffset, "%ld", &tmp_long) == 1)
|
||||
{
|
||||
offset += tmp_long;
|
||||
}
|
||||
|
||||
if(msfoffset && trio_sscanf(msfoffset, "%d:%d:%d", &m, &s, &f) == 3)
|
||||
{
|
||||
offset += ((m * 60 + s) * 75 + f) * sector_mult;
|
||||
}
|
||||
|
||||
track->FileOffset = offset; // Make sure this is set before calling GetSectorCount()!
|
||||
sectors = GetSectorCount(track);
|
||||
//printf("Track: %d, offset: %ld, %ld\n", tracknum, offset, sectors);
|
||||
|
||||
if(length)
|
||||
{
|
||||
tmp_long = sectors;
|
||||
|
||||
if(trio_sscanf(length, "%d:%d:%d", &m, &s, &f) == 3)
|
||||
tmp_long = (m * 60 + s) * 75 + f;
|
||||
else if(track->DIFormat == DI_FORMAT_AUDIO)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
|
||||
tmp_long = strtol(length, &endptr, 10);
|
||||
|
||||
// Error?
|
||||
if(endptr == length)
|
||||
{
|
||||
tmp_long = sectors;
|
||||
}
|
||||
else
|
||||
tmp_long /= 588;
|
||||
|
||||
}
|
||||
|
||||
if(tmp_long > sectors)
|
||||
{
|
||||
fprintf(stderr, "Length specified in TOC file for track %d is too large by %ld sectors!\n", tracknum, (long)(tmp_long - sectors));
|
||||
return false;
|
||||
}
|
||||
sectors = tmp_long;
|
||||
}
|
||||
|
||||
track->FirstFileInstance = 1;
|
||||
track->sectors = sectors;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
{
|
||||
FileWrapper fp(path, FileWrapper::MODE_READ);
|
||||
bool IsTOC = FALSE;
|
||||
char linebuf[512];
|
||||
int32 active_track = -1;
|
||||
int32 AutoTrackInc = 1; // For TOC
|
||||
CDRFILE_TRACK_INFO TmpTrack;
|
||||
std::string file_base, file_ext;
|
||||
|
||||
memset(&TmpTrack, 0, sizeof(TmpTrack));
|
||||
|
||||
MDFN_GetFilePathComponents(path, &base_dir, &file_base, &file_ext);
|
||||
|
||||
if(!strcasecmp(file_ext.c_str(), ".toc"))
|
||||
{
|
||||
puts("TOC file detected.");
|
||||
IsTOC = true;
|
||||
}
|
||||
|
||||
// Check for annoying UTF-8 BOM.
|
||||
if(!IsTOC)
|
||||
{
|
||||
uint8 bom_tmp[3];
|
||||
|
||||
if(fp.read(bom_tmp, 3, false) == 3 && bom_tmp[0] == 0xEF && bom_tmp[1] == 0xBB && bom_tmp[2] == 0xBF)
|
||||
{
|
||||
// Print an annoying error message, but don't actually error out.
|
||||
MDFN_PrintError(_("UTF-8 BOM detected at start of CUE sheet."));
|
||||
}
|
||||
else
|
||||
fp.seek(0, SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
// Assign opposite maximum values so our tests will work!
|
||||
FirstTrack = 99;
|
||||
LastTrack = 0;
|
||||
|
||||
while(fp.get_line(linebuf, 512) != NULL)
|
||||
{
|
||||
char cmdbuf[512], raw_args[512], args[4][512];
|
||||
int argcount = 0;
|
||||
|
||||
raw_args[0] = 0;
|
||||
cmdbuf[0] = 0;
|
||||
|
||||
args[0][0] = args[1][0] = args[2][0] = args[3][0] = 0;
|
||||
|
||||
MDFN_trim(linebuf);
|
||||
|
||||
if(IsTOC)
|
||||
{
|
||||
// Handle TOC format comments
|
||||
char *ss_loc = strstr(linebuf, "//");
|
||||
if(ss_loc)
|
||||
{
|
||||
ss_loc[0] = '\n';
|
||||
ss_loc[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(trio_sscanf(linebuf, "%s %[^\r\n]", cmdbuf, raw_args) < 1)
|
||||
continue; // Skip blank lines
|
||||
|
||||
UnQuotify(UnQuotify(UnQuotify(UnQuotify(raw_args, args[0]), args[1]), args[2]), args[3]);
|
||||
if(args[0][0])
|
||||
{
|
||||
argcount++;
|
||||
if(args[1][0])
|
||||
{
|
||||
argcount++;
|
||||
if(args[2][0])
|
||||
{
|
||||
argcount++;
|
||||
if(args[3][0])
|
||||
{
|
||||
argcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsTOC)
|
||||
{
|
||||
if(!strcasecmp(cmdbuf, "TRACK"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
|
||||
memset(&TmpTrack, 0, sizeof(TmpTrack));
|
||||
active_track = -1;
|
||||
}
|
||||
|
||||
if(AutoTrackInc > 99)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track number: %d"), AutoTrackInc));
|
||||
}
|
||||
|
||||
active_track = AutoTrackInc++;
|
||||
if(active_track < FirstTrack)
|
||||
FirstTrack = active_track;
|
||||
if(active_track > LastTrack)
|
||||
LastTrack = active_track;
|
||||
|
||||
int format_lookup;
|
||||
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
|
||||
{
|
||||
if(!strcasecmp(args[0], DI_CDRDAO_Strings[format_lookup]))
|
||||
{
|
||||
TmpTrack.DIFormat = format_lookup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(format_lookup == _DI_FORMAT_COUNT)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s"), args[0]));
|
||||
}
|
||||
|
||||
if(TmpTrack.DIFormat == DI_FORMAT_AUDIO)
|
||||
TmpTrack.RawAudioMSBFirst = TRUE; // Silly cdrdao...
|
||||
|
||||
if(!strcasecmp(args[1], "RW"))
|
||||
{
|
||||
TmpTrack.SubchannelMode = CDRF_SUBM_RW;
|
||||
throw(MDFN_Error(0, _("\"RW\" format subchannel data not supported, only \"RW_RAW\" is!")));
|
||||
}
|
||||
else if(!strcasecmp(args[1], "RW_RAW"))
|
||||
TmpTrack.SubchannelMode = CDRF_SUBM_RW_RAW;
|
||||
|
||||
} // end to TRACK
|
||||
else if(!strcasecmp(cmdbuf, "SILENCE"))
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "ZERO"))
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "FILE") || !strcasecmp(cmdbuf, "AUDIOFILE"))
|
||||
{
|
||||
const char *binoffset = NULL;
|
||||
const char *msfoffset = NULL;
|
||||
const char *length = NULL;
|
||||
|
||||
if(args[1][0] == '#')
|
||||
{
|
||||
binoffset = args[1] + 1;
|
||||
msfoffset = args[2];
|
||||
length = args[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
msfoffset = args[1];
|
||||
length = args[2];
|
||||
}
|
||||
//printf("%s, %s, %s, %s\n", args[0], binoffset, msfoffset, length);
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "DATAFILE"))
|
||||
{
|
||||
const char *binoffset = NULL;
|
||||
const char *length = NULL;
|
||||
|
||||
if(args[1][0] == '#')
|
||||
{
|
||||
binoffset = args[1] + 1;
|
||||
length = args[2];
|
||||
}
|
||||
else
|
||||
length = args[1];
|
||||
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "INDEX"))
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "PREGAP"))
|
||||
{
|
||||
if(active_track < 0)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf));
|
||||
}
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
} // end to PREGAP
|
||||
else if(!strcasecmp(cmdbuf, "START"))
|
||||
{
|
||||
if(active_track < 0)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf));
|
||||
}
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
} /*********** END TOC HANDLING ************/
|
||||
else // now for CUE sheet handling
|
||||
{
|
||||
if(!strcasecmp(cmdbuf, "FILE"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
|
||||
memset(&TmpTrack, 0, sizeof(TmpTrack));
|
||||
active_track = -1;
|
||||
}
|
||||
|
||||
if(!MDFN_IsFIROPSafe(args[0]))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting.\n"), args[0]));
|
||||
}
|
||||
|
||||
std::string efn = MDFN_EvalFIP(base_dir, args[0]);
|
||||
TmpTrack.fp = new FileStream(efn.c_str(), FileStream::MODE_READ);
|
||||
TmpTrack.FirstFileInstance = 1;
|
||||
|
||||
if(MDFN_GetSettingB("libretro.cd_load_into_ram"))
|
||||
TmpTrack.fp = new MemoryStream(TmpTrack.fp);
|
||||
|
||||
if(!strcasecmp(args[1], "BINARY"))
|
||||
{
|
||||
//TmpTrack.Format = TRACK_FORMAT_DATA;
|
||||
//struct stat stat_buf;
|
||||
//fstat(fileno(TmpTrack.fp), &stat_buf);
|
||||
//TmpTrack.sectors = stat_buf.st_size; // / 2048;
|
||||
}
|
||||
#ifdef NEED_TREMOR
|
||||
else if(!strcasecmp(args[1], "OGG") || !strcasecmp(args[1], "VORBIS") || !strcasecmp(args[1], "WAVE") || !strcasecmp(args[1], "WAV") || !strcasecmp(args[1], "PCM")
|
||||
|| !strcasecmp(args[1], "MPC") || !strcasecmp(args[1], "MP+"))
|
||||
{
|
||||
TmpTrack.AReader = AR_Open(TmpTrack.fp);
|
||||
if(!TmpTrack.AReader)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Unsupported audio track file format: %s\n"), args[0]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw(MDFN_Error(0, _("Unsupported track format: %s\n"), args[1]));
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "TRACK"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
|
||||
TmpTrack.FirstFileInstance = 0;
|
||||
TmpTrack.pregap = 0;
|
||||
TmpTrack.pregap_dv = 0;
|
||||
TmpTrack.postgap = 0;
|
||||
TmpTrack.index[0] = -1;
|
||||
TmpTrack.index[1] = 0;
|
||||
}
|
||||
active_track = atoi(args[0]);
|
||||
|
||||
if(active_track < FirstTrack)
|
||||
FirstTrack = active_track;
|
||||
if(active_track > LastTrack)
|
||||
LastTrack = active_track;
|
||||
|
||||
int format_lookup;
|
||||
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
|
||||
{
|
||||
if(!strcasecmp(args[1], DI_CUE_Strings[format_lookup]))
|
||||
{
|
||||
TmpTrack.DIFormat = format_lookup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(format_lookup == _DI_FORMAT_COUNT)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s\n"), args[0]));
|
||||
}
|
||||
|
||||
if(active_track < 0 || active_track > 99)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track number: %d\n"), active_track));
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "INDEX"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[1], "%d:%d:%d", &m, &s, &f);
|
||||
|
||||
if(!strcasecmp(args[0], "01") || !strcasecmp(args[0], "1"))
|
||||
TmpTrack.index[1] = (m * 60 + s) * 75 + f;
|
||||
else if(!strcasecmp(args[0], "00") || !strcasecmp(args[0], "0"))
|
||||
TmpTrack.index[0] = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "PREGAP"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "POSTGAP"))
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.postgap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "REM"))
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "CDTEXTFILE") || !strcasecmp(cmdbuf, "FLAGS") || !strcasecmp(cmdbuf, "CATALOG") || !strcasecmp(cmdbuf, "ISRC") ||
|
||||
!strcasecmp(cmdbuf, "TITLE") || !strcasecmp(cmdbuf, "PERFORMER") || !strcasecmp(cmdbuf, "SONGWRITER"))
|
||||
{
|
||||
MDFN_printf(_("Unsupported CUE sheet directive: \"%s\".\n"), cmdbuf); // FIXME, generic logger passed by pointer to constructor
|
||||
}
|
||||
else
|
||||
{
|
||||
throw MDFN_Error(0, _("Unknown CUE sheet directive \"%s\".\n"), cmdbuf);
|
||||
}
|
||||
} // end of CUE sheet handling
|
||||
} // end of fgets() loop
|
||||
|
||||
if(active_track >= 0)
|
||||
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
|
||||
|
||||
if(FirstTrack > LastTrack)
|
||||
{
|
||||
throw(MDFN_Error(0, _("No tracks found!\n")));
|
||||
}
|
||||
|
||||
FirstTrack = FirstTrack;
|
||||
NumTracks = 1 + LastTrack - FirstTrack;
|
||||
|
||||
int32 RunningLBA = 0;
|
||||
int32 LastIndex = 0;
|
||||
(void)LastIndex;
|
||||
long FileOffset = 0;
|
||||
|
||||
for(int x = FirstTrack; x < (FirstTrack + NumTracks); x++)
|
||||
{
|
||||
if(Tracks[x].DIFormat == DI_FORMAT_AUDIO)
|
||||
Tracks[x].Format = CD_TRACK_FORMAT_AUDIO;
|
||||
else
|
||||
Tracks[x].Format = CD_TRACK_FORMAT_DATA;
|
||||
|
||||
if(IsTOC)
|
||||
{
|
||||
RunningLBA += Tracks[x].pregap;
|
||||
Tracks[x].LBA = RunningLBA;
|
||||
RunningLBA += Tracks[x].sectors;
|
||||
RunningLBA += Tracks[x].postgap;
|
||||
}
|
||||
else // else handle CUE sheet...
|
||||
{
|
||||
if(Tracks[x].FirstFileInstance)
|
||||
{
|
||||
LastIndex = 0;
|
||||
FileOffset = 0;
|
||||
}
|
||||
|
||||
RunningLBA += Tracks[x].pregap;
|
||||
|
||||
Tracks[x].pregap_dv = 0;
|
||||
|
||||
if(Tracks[x].index[0] != -1)
|
||||
Tracks[x].pregap_dv = Tracks[x].index[1] - Tracks[x].index[0];
|
||||
|
||||
FileOffset += Tracks[x].pregap_dv * DI_Size_Table[Tracks[x].DIFormat];
|
||||
|
||||
RunningLBA += Tracks[x].pregap_dv;
|
||||
|
||||
Tracks[x].LBA = RunningLBA;
|
||||
|
||||
// Make sure FileOffset this is set before the call to GetSectorCount()
|
||||
Tracks[x].FileOffset = FileOffset;
|
||||
Tracks[x].sectors = GetSectorCount(&Tracks[x]);
|
||||
|
||||
if((x + 1) >= (FirstTrack + NumTracks) || Tracks[x+1].FirstFileInstance)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix the sector count if we have multiple tracks per one binary image file.
|
||||
if(Tracks[x + 1].index[0] == -1)
|
||||
Tracks[x].sectors = Tracks[x + 1].index[1] - Tracks[x].index[1];
|
||||
else
|
||||
Tracks[x].sectors = Tracks[x + 1].index[0] - Tracks[x].index[1]; //Tracks[x + 1].index - Tracks[x].index;
|
||||
}
|
||||
|
||||
//printf("Poo: %d %d\n", x, Tracks[x].sectors);
|
||||
RunningLBA += Tracks[x].sectors;
|
||||
RunningLBA += Tracks[x].postgap;
|
||||
|
||||
//printf("%d, %ld %d %d %d %d\n", x, FileOffset, Tracks[x].index, Tracks[x].pregap, Tracks[x].sectors, Tracks[x].LBA);
|
||||
|
||||
FileOffset += Tracks[x].sectors * DI_Size_Table[Tracks[x].DIFormat];
|
||||
} // end to cue sheet handling
|
||||
} // end to track loop
|
||||
|
||||
total_sectors = RunningLBA;
|
||||
}
|
||||
|
||||
void CDAccess_Image::Cleanup(void)
|
||||
{
|
||||
for(int32 track = 0; track < 100; track++)
|
||||
{
|
||||
CDRFILE_TRACK_INFO *this_track = &Tracks[track];
|
||||
|
||||
if(this_track->FirstFileInstance)
|
||||
{
|
||||
if(Tracks[track].AReader)
|
||||
{
|
||||
delete Tracks[track].AReader;
|
||||
Tracks[track].AReader = NULL;
|
||||
}
|
||||
|
||||
if(this_track->fp)
|
||||
{
|
||||
delete this_track->fp;
|
||||
this_track->fp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CDAccess_Image::CDAccess_Image(const char *path, bool image_memcache) : NumTracks(0), FirstTrack(0), LastTrack(0), total_sectors(0)
|
||||
{
|
||||
memset(Tracks, 0, sizeof(Tracks));
|
||||
ImageOpen(path, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
}
|
||||
|
||||
CDAccess_Image::~CDAccess_Image()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void CDAccess_Image::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
{
|
||||
bool TrackFound = FALSE;
|
||||
uint8 SimuQ[0xC];
|
||||
|
||||
memset(buf + 2352, 0, 96);
|
||||
|
||||
MakeSubPQ(lba, buf + 2352);
|
||||
|
||||
subq_deinterleave(buf + 2352, SimuQ);
|
||||
|
||||
for(int32 track = FirstTrack; track < (FirstTrack + NumTracks); track++)
|
||||
{
|
||||
CDRFILE_TRACK_INFO *ct = &Tracks[track];
|
||||
|
||||
if(lba >= (ct->LBA - ct->pregap_dv - ct->pregap) && lba < (ct->LBA + ct->sectors + ct->postgap))
|
||||
{
|
||||
TrackFound = TRUE;
|
||||
|
||||
// Handle pregap and postgap reading
|
||||
if(lba < (ct->LBA - ct->pregap_dv) || lba >= (ct->LBA + ct->sectors))
|
||||
{
|
||||
//printf("Pre/post-gap read, LBA=%d(LBA-track_start_LBA=%d)\n", lba, lba - ct->LBA);
|
||||
memset(buf, 0, 2352); // Null sector data, per spec
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ct->AReader)
|
||||
{
|
||||
int16 AudioBuf[588 * 2];
|
||||
int frames_read = ct->AReader->Read((ct->FileOffset / 4) + (lba - ct->LBA) * 588, AudioBuf, 588);
|
||||
|
||||
ct->LastSamplePos += frames_read;
|
||||
|
||||
if(frames_read < 0 || frames_read > 588) // This shouldn't happen.
|
||||
{
|
||||
printf("Error: frames_read out of range: %d\n", frames_read);
|
||||
frames_read = 0;
|
||||
}
|
||||
|
||||
if(frames_read < 588)
|
||||
memset((uint8 *)AudioBuf + frames_read * 2 * sizeof(int16), 0, (588 - frames_read) * 2 * sizeof(int16));
|
||||
|
||||
for(int i = 0; i < 588 * 2; i++)
|
||||
MDFN_en16lsb(buf + i * 2, AudioBuf[i]);
|
||||
}
|
||||
else // Binary, woo.
|
||||
{
|
||||
long SeekPos = ct->FileOffset;
|
||||
long LBARelPos = lba - ct->LBA;
|
||||
|
||||
SeekPos += LBARelPos * DI_Size_Table[ct->DIFormat];
|
||||
|
||||
if(ct->SubchannelMode)
|
||||
SeekPos += 96 * (lba - ct->LBA);
|
||||
|
||||
ct->fp->seek(SeekPos, SEEK_SET);
|
||||
|
||||
switch(ct->DIFormat)
|
||||
{
|
||||
case DI_FORMAT_AUDIO:
|
||||
ct->fp->read(buf, 2352);
|
||||
|
||||
if(ct->RawAudioMSBFirst)
|
||||
Endian_A16_Swap(buf, 588 * 2);
|
||||
break;
|
||||
|
||||
case DI_FORMAT_MODE1:
|
||||
ct->fp->read(buf + 12 + 3 + 1, 2048);
|
||||
encode_mode1_sector(lba + 150, buf);
|
||||
break;
|
||||
|
||||
case DI_FORMAT_MODE1_RAW:
|
||||
case DI_FORMAT_MODE2_RAW:
|
||||
ct->fp->read(buf, 2352);
|
||||
break;
|
||||
|
||||
case DI_FORMAT_MODE2:
|
||||
ct->fp->read(buf + 16, 2336);
|
||||
encode_mode2_sector(lba + 150, buf);
|
||||
break;
|
||||
|
||||
|
||||
// FIXME: M2F1, M2F2, does sub-header come before or after user data(standards say before, but I wonder
|
||||
// about cdrdao...).
|
||||
case DI_FORMAT_MODE2_FORM1:
|
||||
ct->fp->read(buf + 24, 2048);
|
||||
//encode_mode2_form1_sector(lba + 150, buf);
|
||||
break;
|
||||
|
||||
case DI_FORMAT_MODE2_FORM2:
|
||||
ct->fp->read(buf + 24, 2324);
|
||||
//encode_mode2_form2_sector(lba + 150, buf);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if(ct->SubchannelMode)
|
||||
ct->fp->read(buf + 2352, 96);
|
||||
}
|
||||
} // end if audible part of audio track read.
|
||||
break;
|
||||
} // End if LBA is in range
|
||||
} // end track search loop
|
||||
|
||||
if(!TrackFound)
|
||||
{
|
||||
fprintf(stderr, "Could not find track for sector %u!\n", lba);
|
||||
}
|
||||
}
|
||||
|
||||
void CDAccess_Image::MakeSubPQ(int32 lba, uint8 *SubPWBuf)
|
||||
{
|
||||
uint8 buf[0xC];
|
||||
int32 track;
|
||||
uint32 lba_relative;
|
||||
uint32 ma, sa, fa;
|
||||
uint32 m, s, f;
|
||||
uint8 pause_or = 0x00;
|
||||
bool track_found = FALSE;
|
||||
|
||||
for(track = FirstTrack; track < (FirstTrack + NumTracks); track++)
|
||||
{
|
||||
if(lba >= (Tracks[track].LBA - Tracks[track].pregap_dv - Tracks[track].pregap) && lba < (Tracks[track].LBA + Tracks[track].sectors + Tracks[track].postgap))
|
||||
{
|
||||
track_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%d %d\n", Tracks[1].LBA, Tracks[1].sectors);
|
||||
|
||||
if(!track_found)
|
||||
{
|
||||
printf("MakeSubPQ error for sector %u!", lba);
|
||||
track = FirstTrack;
|
||||
}
|
||||
|
||||
lba_relative = abs((int32)lba - Tracks[track].LBA);
|
||||
|
||||
f = (lba_relative % 75);
|
||||
s = ((lba_relative / 75) % 60);
|
||||
m = (lba_relative / 75 / 60);
|
||||
|
||||
fa = (lba + 150) % 75;
|
||||
sa = ((lba + 150) / 75) % 60;
|
||||
ma = ((lba + 150) / 75 / 60);
|
||||
|
||||
uint8 adr = 0x1; // Q channel data encodes position
|
||||
uint8 control = (Tracks[track].Format == CD_TRACK_FORMAT_AUDIO) ? 0x00 : 0x04;
|
||||
|
||||
// Handle pause(D7 of interleaved subchannel byte) bit, should be set to 1 when in pregap or postgap.
|
||||
if((lba < Tracks[track].LBA) || (lba >= Tracks[track].LBA + Tracks[track].sectors))
|
||||
{
|
||||
//printf("pause_or = 0x80 --- %d\n", lba);
|
||||
pause_or = 0x80;
|
||||
}
|
||||
|
||||
// Handle pregap between audio->data track
|
||||
{
|
||||
int32 pg_offset = (int32)lba - Tracks[track].LBA;
|
||||
|
||||
// If we're more than 2 seconds(150 sectors) from the real "start" of the track/INDEX 01, and the track is a data track,
|
||||
// and the preceding track is an audio track, encode it as audio.
|
||||
if(pg_offset < -150)
|
||||
{
|
||||
if(Tracks[track].Format == CD_TRACK_FORMAT_DATA && (FirstTrack < track) &&
|
||||
Tracks[track - 1].Format == CD_TRACK_FORMAT_AUDIO)
|
||||
{
|
||||
//printf("Pregap part 1 audio->data: lba=%d track_lba=%d\n", lba, Tracks[track].LBA);
|
||||
control = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
memset(buf, 0, 0xC);
|
||||
buf[0] = (adr << 0) | (control << 4);
|
||||
buf[1] = U8_to_BCD(track);
|
||||
|
||||
if(lba < Tracks[track].LBA) // Index is 00 in pregap
|
||||
buf[2] = U8_to_BCD(0x00);
|
||||
else
|
||||
buf[2] = U8_to_BCD(0x01);
|
||||
|
||||
// Track relative MSF address
|
||||
buf[3] = U8_to_BCD(m);
|
||||
buf[4] = U8_to_BCD(s);
|
||||
buf[5] = U8_to_BCD(f);
|
||||
|
||||
buf[6] = 0; // Zerroooo
|
||||
|
||||
// Absolute MSF address
|
||||
buf[7] = U8_to_BCD(ma);
|
||||
buf[8] = U8_to_BCD(sa);
|
||||
buf[9] = U8_to_BCD(fa);
|
||||
|
||||
subq_generate_checksum(buf);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
SubPWBuf[i] |= (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | pause_or;
|
||||
}
|
||||
|
||||
void CDAccess_Image::Read_TOC(TOC *toc)
|
||||
{
|
||||
toc->Clear();
|
||||
|
||||
toc->first_track = FirstTrack;
|
||||
toc->last_track = FirstTrack + NumTracks - 1;
|
||||
toc->disc_type = DISC_TYPE_CDDA_OR_M1; // FIXME
|
||||
|
||||
for(int i = toc->first_track; i <= toc->last_track; i++)
|
||||
{
|
||||
toc->tracks[i].lba = Tracks[i].LBA;
|
||||
toc->tracks[i].adr = ADR_CURPOS;
|
||||
toc->tracks[i].control = 0x0;
|
||||
|
||||
if(Tracks[i].Format != CD_TRACK_FORMAT_AUDIO)
|
||||
toc->tracks[i].control |= 0x4;
|
||||
}
|
||||
|
||||
toc->tracks[100].lba = total_sectors;
|
||||
toc->tracks[100].adr = ADR_CURPOS;
|
||||
toc->tracks[100].control = 0x00; // Audio...
|
||||
|
||||
// Convenience leadout track duplication.
|
||||
if(toc->last_track < 99)
|
||||
toc->tracks[toc->last_track + 1] = toc->tracks[100];
|
||||
}
|
||||
|
||||
bool CDAccess_Image::Is_Physical(void)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
void CDAccess_Image::Eject(bool eject_status)
|
||||
{
|
||||
|
||||
}
|
||||
|
68
mednafen/cdrom-0928/CDAccess_Image.h
Normal file
68
mednafen/cdrom-0928/CDAccess_Image.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef __MDFN_CDACCESS_IMAGE_H
|
||||
#define __MDFN_CDACCESS_IMAGE_H
|
||||
|
||||
class Stream;
|
||||
class AudioReader;
|
||||
|
||||
struct CDRFILE_TRACK_INFO
|
||||
{
|
||||
int32 LBA;
|
||||
|
||||
CDUtility::CD_Track_Format_t Format;
|
||||
uint32 DIFormat;
|
||||
|
||||
int32 pregap;
|
||||
int32 pregap_dv;
|
||||
|
||||
int32 postgap;
|
||||
|
||||
int32 index[2];
|
||||
|
||||
int32 sectors; // Not including pregap sectors!
|
||||
Stream *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
long FileOffset;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
|
||||
AudioReader *AReader;
|
||||
};
|
||||
|
||||
class CDAccess_Image : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_Image(const char *path, bool image_memcache);
|
||||
virtual ~CDAccess_Image();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void);
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
private:
|
||||
|
||||
int32 NumTracks;
|
||||
int32 FirstTrack;
|
||||
int32 LastTrack;
|
||||
int32 total_sectors;
|
||||
CDRFILE_TRACK_INFO Tracks[100]; // Track #0(HMM?) through 99
|
||||
|
||||
std::string base_dir;
|
||||
|
||||
void ImageOpen(const char *path, bool image_memcache);
|
||||
void Cleanup(void);
|
||||
|
||||
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
|
||||
void MakeSubPQ(int32 lba, uint8 *SubPWBuf);
|
||||
|
||||
bool ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const char *filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache);
|
||||
uint32 GetSectorCount(CDRFILE_TRACK_INFO *track);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
162
mednafen/cdrom-0928/CDUtility.cpp
Normal file
162
mednafen/cdrom-0928/CDUtility.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
#include "../mednafen.h"
|
||||
#include "CDUtility.h"
|
||||
#include "lec.h"
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
|
||||
// lookup table for crc calculation
|
||||
static uint16 subq_crctab[256] =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
|
||||
0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
|
||||
0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
|
||||
0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
|
||||
0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
|
||||
0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
|
||||
0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
|
||||
0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
|
||||
0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
|
||||
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
|
||||
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
|
||||
0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
|
||||
0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
|
||||
0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
|
||||
0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
|
||||
0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
|
||||
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
|
||||
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
|
||||
0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
|
||||
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
|
||||
0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
|
||||
0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
|
||||
0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
|
||||
0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
|
||||
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
|
||||
|
||||
static bool CDUtility_Inited = false;
|
||||
|
||||
void CDUtility_Init(void)
|
||||
{
|
||||
if(!CDUtility_Inited)
|
||||
{
|
||||
Init_LEC_Correct();
|
||||
|
||||
CDUtility_Inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode0_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
bool edc_check(const uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(CheckEDC(sector_data, xa));
|
||||
}
|
||||
|
||||
bool edc_lec_check_correct(uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(ValidateRawSector(sector_data, xa));
|
||||
}
|
||||
|
||||
|
||||
bool subq_check_checksum(const uint8 *SubQBuf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
uint16 stored_crc = 0;
|
||||
|
||||
stored_crc = SubQBuf[0xA] << 8;
|
||||
stored_crc |= SubQBuf[0xB];
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8);
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
return(crc == stored_crc);
|
||||
}
|
||||
|
||||
void subq_generate_checksum(uint8 *buf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
|
||||
|
||||
// Checksum
|
||||
buf[0xa] = ~(crc >> 8);
|
||||
buf[0xb] = ~(crc);
|
||||
}
|
||||
|
||||
void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
|
||||
{
|
||||
memset(qbuf, 0, 0xC);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
{
|
||||
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
}
|
210
mednafen/cdrom-0928/CDUtility.h
Normal file
210
mednafen/cdrom-0928/CDUtility.h
Normal file
@ -0,0 +1,210 @@
|
||||
#ifndef __MDFN_CDROM_CDUTILITY_H
|
||||
#define __MDFN_CDROM_CDUTILITY_H
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
// Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions.
|
||||
// It will also be called automatically if needed for the first time a function in this namespace that requires
|
||||
// the initialization function to be called is called, for potential
|
||||
// usage in constructors of statically-declared objects.
|
||||
void CDUtility_Init(void);
|
||||
|
||||
// Quick definitions here:
|
||||
//
|
||||
// ABA - Absolute block address, synonymous to absolute MSF
|
||||
// aba = (m_a * 60 * 75) + (s_a * 75) + f_a
|
||||
//
|
||||
// LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors)
|
||||
// lba = aba - 150
|
||||
|
||||
|
||||
// Track formats(more abstract and simple)
|
||||
typedef enum
|
||||
{
|
||||
CD_TRACK_FORMAT_AUDIO = 0x00,
|
||||
CD_TRACK_FORMAT_DATA = 0x01,
|
||||
//CDRF_FORMAT_CDI = 0x02
|
||||
} CD_Track_Format_t;
|
||||
|
||||
enum
|
||||
{
|
||||
ADR_NOQINFO = 0x00,
|
||||
ADR_CURPOS = 0x01,
|
||||
ADR_MCN = 0x02,
|
||||
ADR_ISRC = 0x03
|
||||
};
|
||||
|
||||
|
||||
struct TOC_Track
|
||||
{
|
||||
uint8 adr;
|
||||
uint8 control;
|
||||
uint32 lba;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DISC_TYPE_CDDA_OR_M1 = 0x00,
|
||||
DISC_TYPE_CD_I = 0x10,
|
||||
DISC_TYPE_CD_XA = 0x20
|
||||
};
|
||||
|
||||
struct TOC
|
||||
{
|
||||
INLINE TOC()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
INLINE void Clear(void)
|
||||
{
|
||||
first_track = last_track = 0;
|
||||
disc_type = 0;
|
||||
|
||||
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
|
||||
}
|
||||
|
||||
INLINE int FindTrackByLBA(uint32 LBA)
|
||||
{
|
||||
for(int32 track = first_track; track <= (last_track + 1); track++)
|
||||
{
|
||||
if(track == (last_track + 1))
|
||||
{
|
||||
if(LBA < tracks[100].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(LBA < tracks[track].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint8 first_track;
|
||||
uint8 last_track;
|
||||
uint8 disc_type;
|
||||
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
|
||||
// Also, for convenience, tracks[last_track + 1] will always refer
|
||||
// to the leadout track(even if last_track < 99, IE the leadout track details are duplicated).
|
||||
};
|
||||
|
||||
//
|
||||
// Address conversion functions.
|
||||
//
|
||||
static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a)
|
||||
{
|
||||
return(f_a + 75 * s_a + 75 * 60 * m_a);
|
||||
}
|
||||
|
||||
static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
*m_a = aba / 75 / 60;
|
||||
*s_a = (aba - *m_a * 75 * 60) / 75;
|
||||
*f_a = aba - (*m_a * 75 * 60) - (*s_a * 75);
|
||||
}
|
||||
|
||||
static INLINE int32 ABA_to_LBA(uint32 aba)
|
||||
{
|
||||
return(aba - 150);
|
||||
}
|
||||
|
||||
static INLINE uint32 LBA_to_ABA(int32 lba)
|
||||
{
|
||||
return(lba + 150);
|
||||
}
|
||||
|
||||
static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a)
|
||||
{
|
||||
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
|
||||
}
|
||||
|
||||
static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
|
||||
}
|
||||
|
||||
//
|
||||
// BCD conversion functions
|
||||
//
|
||||
static INLINE bool BCD_is_valid(uint8 bcd_number)
|
||||
{
|
||||
if((bcd_number & 0xF0) >= 0xA0)
|
||||
return(false);
|
||||
|
||||
if((bcd_number & 0x0F) >= 0x0A)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static INLINE uint8 BCD_to_U8(uint8 bcd_number)
|
||||
{
|
||||
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
|
||||
}
|
||||
|
||||
static INLINE uint8 U8_to_BCD(uint8 num)
|
||||
{
|
||||
return( ((num / 10) << 4) + (num % 10) );
|
||||
}
|
||||
|
||||
// should always perform the conversion, even if the bcd number is invalid.
|
||||
static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number)
|
||||
{
|
||||
*out_number = BCD_to_U8(bcd_number);
|
||||
|
||||
if(!BCD_is_valid(bcd_number))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Sector data encoding functions(to full 2352 bytes raw sector).
|
||||
//
|
||||
// sector_data must be able to contain at least 2352 bytes.
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data);
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16
|
||||
|
||||
//
|
||||
// User data error detection and correction
|
||||
//
|
||||
|
||||
// Check EDC of a mode 1 or mode 2 form 1 sector.
|
||||
// Returns "true" if checksum is ok(matches).
|
||||
// Returns "false" if checksum mismatch.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_check(const uint8 *sector_data, bool xa);
|
||||
|
||||
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
|
||||
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||
// Returns "false" if errors couldn't be corrected.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_lec_check_correct(uint8 *sector_data, bool xa);
|
||||
|
||||
//
|
||||
// Subchannel(Q in particular) functions
|
||||
//
|
||||
|
||||
// Returns false on checksum mismatch, true on match.
|
||||
bool subq_check_checksum(const uint8 *subq_buf);
|
||||
|
||||
// Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position
|
||||
// in subq_buf.
|
||||
void subq_generate_checksum(uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
|
||||
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
|
||||
|
||||
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
|
||||
// Only valid for ADR_CURPOS.
|
||||
// subq_input must pass subq_check_checksum().
|
||||
// TODO
|
||||
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
|
||||
}
|
||||
|
||||
#endif
|
7
mednafen/cdrom-0928/Makefile.am.inc
Normal file
7
mednafen/cdrom-0928/Makefile.am.inc
Normal file
@ -0,0 +1,7 @@
|
||||
mednafen_SOURCES += cdrom/audioreader.cpp cdrom/cdromif.cpp cdrom/scsicd.cpp cdrom/pcecd.cpp
|
||||
mednafen_SOURCES += cdrom/CDUtility.cpp cdrom/crc32.cpp cdrom/galois.cpp cdrom/l-ec.cpp cdrom/recover-raw.cpp
|
||||
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp
|
||||
|
||||
if HAVE_LIBCDIO
|
||||
mednafen_SOURCES += cdrom/CDAccess_Physical.cpp
|
||||
endif
|
6
mednafen/cdrom-0928/SimpleFIFO.cpp
Normal file
6
mednafen/cdrom-0928/SimpleFIFO.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "../mednafen.h"
|
||||
#include "SimpleFIFO.h"
|
||||
|
||||
|
||||
|
||||
|
99
mednafen/cdrom-0928/SimpleFIFO.h
Normal file
99
mednafen/cdrom-0928/SimpleFIFO.h
Normal file
@ -0,0 +1,99 @@
|
||||
#ifndef __MDFN_SIMPLEFIFO_H
|
||||
#define __MDFN_SIMPLEFIFO_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../math_ops.h"
|
||||
|
||||
template<typename T>
|
||||
class SimpleFIFO
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
SimpleFIFO(uint32 the_size) // Size should be a power of 2!
|
||||
{
|
||||
data.resize(round_up_pow2(the_size));
|
||||
size = the_size;
|
||||
read_pos = 0;
|
||||
write_pos = 0;
|
||||
in_count = 0;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
INLINE ~SimpleFIFO()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
INLINE uint32 CanRead(void)
|
||||
{
|
||||
return(in_count);
|
||||
}
|
||||
|
||||
INLINE uint32 CanWrite(void)
|
||||
{
|
||||
return(size - in_count);
|
||||
}
|
||||
|
||||
INLINE T ReadUnit(bool peek = false)
|
||||
{
|
||||
T ret;
|
||||
|
||||
ret = data[read_pos];
|
||||
|
||||
if(!peek)
|
||||
{
|
||||
read_pos = (read_pos + 1) & (data.size() - 1);
|
||||
in_count--;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
INLINE uint8 ReadByte(bool peek = false)
|
||||
{
|
||||
return(ReadUnit(peek));
|
||||
}
|
||||
|
||||
INLINE void Write(const T *happy_data, uint32 happy_count)
|
||||
{
|
||||
while(happy_count)
|
||||
{
|
||||
data[write_pos] = *happy_data;
|
||||
|
||||
write_pos = (write_pos + 1) & (data.size() - 1);
|
||||
in_count++;
|
||||
happy_data++;
|
||||
happy_count--;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void WriteUnit(const T& wr_data)
|
||||
{
|
||||
Write(&wr_data, 1);
|
||||
}
|
||||
|
||||
INLINE void WriteByte(const T& wr_data)
|
||||
{
|
||||
Write(&wr_data, 1);
|
||||
}
|
||||
|
||||
|
||||
INLINE void Flush(void)
|
||||
{
|
||||
read_pos = 0;
|
||||
write_pos = 0;
|
||||
in_count = 0;
|
||||
}
|
||||
|
||||
//private:
|
||||
std::vector<T> data;
|
||||
uint32 size;
|
||||
uint32 read_pos; // Read position
|
||||
uint32 write_pos; // Write position
|
||||
uint32 in_count; // Number of units in the FIFO
|
||||
};
|
||||
|
||||
|
||||
#endif
|
206
mednafen/cdrom-0928/audioreader.cpp
Normal file
206
mednafen/cdrom-0928/audioreader.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
|
||||
// to it for as long as the AudioReader object exists.
|
||||
|
||||
// Don't allow exceptions to propagate into the vorbis/musepack/etc. libraries, as it could easily leave the state of the library's decoder "object" in an
|
||||
// inconsistent state, which would cause all sorts of unfun when we try to destroy it while handling the exception farther up.
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
#include "audioreader.h"
|
||||
|
||||
#include "../tremor/ivorbisfile.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../general.h"
|
||||
#include "../mednafen-endian.h"
|
||||
|
||||
AudioReader::AudioReader() : LastReadPos(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AudioReader::~AudioReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int64 AudioReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
abort();
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool AudioReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
abort();
|
||||
return(false);
|
||||
}
|
||||
|
||||
int64 AudioReader::FrameCount(void)
|
||||
{
|
||||
abort();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef NEED_TREMOR
|
||||
|
||||
class OggVorbisReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
OggVorbisReader(Stream *fp);
|
||||
~OggVorbisReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
OggVorbis_File ovfile;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
|
||||
static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
if(!size)
|
||||
return(0);
|
||||
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
|
||||
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int iov_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long iov_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
return fw->tell();
|
||||
}
|
||||
|
||||
OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
ov_callbacks cb;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.read_func = iov_read_func;
|
||||
cb.seek_func = iov_seek_func;
|
||||
cb.close_func = iov_close_func;
|
||||
cb.tell_func = iov_tell_func;
|
||||
|
||||
fp->seek(0, SEEK_SET);
|
||||
if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
|
||||
throw(0);
|
||||
}
|
||||
|
||||
OggVorbisReader::~OggVorbisReader()
|
||||
{
|
||||
ov_clear(&ovfile);
|
||||
}
|
||||
|
||||
int64 OggVorbisReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
uint8 *tw_buf = (uint8 *)buffer;
|
||||
int cursection = 0;
|
||||
long toread = frames * sizeof(int16) * 2;
|
||||
|
||||
while(toread > 0)
|
||||
{
|
||||
long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
|
||||
|
||||
if(didread == 0)
|
||||
break;
|
||||
|
||||
tw_buf = (uint8 *)tw_buf + didread;
|
||||
toread -= didread;
|
||||
}
|
||||
|
||||
return(frames - toread / sizeof(int16) / 2);
|
||||
}
|
||||
|
||||
bool OggVorbisReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
ov_pcm_seek(&ovfile, frame_offset);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 OggVorbisReader::FrameCount(void)
|
||||
{
|
||||
return(ov_pcm_total(&ovfile, -1));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
AudioReader *AR_Open(Stream *fp)
|
||||
{
|
||||
#ifdef NEED_TREMOR
|
||||
try
|
||||
{
|
||||
return new OggVorbisReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
41
mednafen/cdrom-0928/audioreader.h
Normal file
41
mednafen/cdrom-0928/audioreader.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __MDFN_AUDIOREADER_H
|
||||
#define __MDFN_AUDIOREADER_H
|
||||
|
||||
#include "../Stream.h"
|
||||
|
||||
class AudioReader
|
||||
{
|
||||
public:
|
||||
AudioReader();
|
||||
virtual ~AudioReader();
|
||||
|
||||
virtual int64 FrameCount(void);
|
||||
INLINE int64 Read(int64 frame_offset, int16 *buffer, int64 frames)
|
||||
{
|
||||
int64 ret;
|
||||
|
||||
{
|
||||
if(LastReadPos != frame_offset)
|
||||
{
|
||||
if(!Seek_(frame_offset))
|
||||
return(0);
|
||||
LastReadPos = frame_offset;
|
||||
}
|
||||
}
|
||||
ret = Read_(buffer, frames);
|
||||
LastReadPos += ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual int64 Read_(int16 *buffer, int64 frames);
|
||||
virtual bool Seek_(int64 frame_offset);
|
||||
|
||||
int64 LastReadPos;
|
||||
};
|
||||
|
||||
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
|
||||
// to it for as long as the AudioReader object exists.
|
||||
AudioReader *AR_Open(Stream *fp);
|
||||
|
||||
#endif
|
604
mednafen/cdrom-0928/cdromif.cpp
Normal file
604
mednafen/cdrom-0928/cdromif.cpp
Normal file
@ -0,0 +1,604 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <trio/trio.h>
|
||||
#include "cdromif.h"
|
||||
#include "CDAccess.h"
|
||||
#include "../general.h"
|
||||
#include "../mednafen-driver.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
enum
|
||||
{
|
||||
// Status/Error messages
|
||||
CDIF_MSG_DONE = 0, // Read -> emu. args: No args.
|
||||
CDIF_MSG_INFO, // Read -> emu. args: str_message
|
||||
CDIF_MSG_FATAL_ERROR, // Read -> emu. args: *TODO ARGS*
|
||||
|
||||
//
|
||||
// Command messages.
|
||||
//
|
||||
CDIF_MSG_DIEDIEDIE, // Emu -> read
|
||||
|
||||
CDIF_MSG_READ_SECTOR, /* Emu -> read
|
||||
args[0] = lba
|
||||
*/
|
||||
|
||||
CDIF_MSG_EJECT, // Emu -> read, args[0]; 0=insert, 1=eject
|
||||
};
|
||||
|
||||
CDIF::CDIF() : UnrecoverableError(false), is_phys_cache(false), DiscEjected(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF::~CDIF()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
CDIF_Message::CDIF_Message()
|
||||
{
|
||||
message = 0;
|
||||
|
||||
memset(args, 0, sizeof(args));
|
||||
}
|
||||
|
||||
CDIF_Message::CDIF_Message(unsigned int message_, uint32 arg0, uint32 arg1, uint32 arg2, uint32 arg3)
|
||||
{
|
||||
message = message_;
|
||||
args[0] = arg0;
|
||||
args[1] = arg1;
|
||||
args[2] = arg2;
|
||||
args[3] = arg3;
|
||||
}
|
||||
|
||||
CDIF_Message::CDIF_Message(unsigned int message_, const std::string &str)
|
||||
{
|
||||
message = message_;
|
||||
str_message = str;
|
||||
}
|
||||
|
||||
CDIF_Message::~CDIF_Message()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF_Queue::CDIF_Queue()
|
||||
{
|
||||
ze_mutex = MDFND_CreateMutex();
|
||||
}
|
||||
|
||||
CDIF_Queue::~CDIF_Queue()
|
||||
{
|
||||
MDFND_DestroyMutex(ze_mutex);
|
||||
}
|
||||
|
||||
// Returns FALSE if message not read, TRUE if it was read. Will always return TRUE if "blocking" is set.
|
||||
// Will throw MDFN_Error if the read message code is CDIF_MSG_FATAL_ERROR
|
||||
bool CDIF_Queue::Read(CDIF_Message *message, bool blocking)
|
||||
{
|
||||
TryAgain:
|
||||
|
||||
MDFND_LockMutex(ze_mutex);
|
||||
|
||||
if(ze_queue.size() > 0)
|
||||
{
|
||||
*message = ze_queue.front();
|
||||
ze_queue.pop();
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
|
||||
if(message->message == CDIF_MSG_FATAL_ERROR)
|
||||
throw MDFN_Error(0, "%s", message->str_message.c_str());
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
else if(blocking)
|
||||
{
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
MDFND_Sleep(1);
|
||||
goto TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
return(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void CDIF_Queue::Write(const CDIF_Message &message)
|
||||
{
|
||||
MDFND_LockMutex(ze_mutex);
|
||||
|
||||
ze_queue.push(message);
|
||||
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
}
|
||||
|
||||
void CDIF_MT::RT_EjectDisc(bool eject_status, bool skip_actual_eject)
|
||||
{
|
||||
int32 old_de = DiscEjected;
|
||||
|
||||
DiscEjected = eject_status;
|
||||
|
||||
if(old_de != DiscEjected)
|
||||
{
|
||||
if(!skip_actual_eject)
|
||||
disc_cdaccess->Eject(eject_status);
|
||||
|
||||
if(!eject_status) // Re-read the TOC
|
||||
{
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
|
||||
SBWritePos = 0;
|
||||
ra_lba = 0;
|
||||
ra_count = 0;
|
||||
last_read_lba = ~0U;
|
||||
memset(SectorBuffers, 0, SBSize * sizeof(CDIF_Sector_Buffer));
|
||||
}
|
||||
}
|
||||
|
||||
struct RTS_Args
|
||||
{
|
||||
CDIF_MT *cdif_ptr;
|
||||
};
|
||||
|
||||
static int ReadThreadStart_C(void *v_arg)
|
||||
{
|
||||
RTS_Args *args = (RTS_Args *)v_arg;
|
||||
|
||||
return args->cdif_ptr->ReadThreadStart();
|
||||
}
|
||||
|
||||
int CDIF_MT::ReadThreadStart()
|
||||
{
|
||||
bool Running = TRUE;
|
||||
|
||||
DiscEjected = true;
|
||||
SBWritePos = 0;
|
||||
ra_lba = 0;
|
||||
ra_count = 0;
|
||||
last_read_lba = ~0U;
|
||||
|
||||
RT_EjectDisc(false, true);
|
||||
|
||||
is_phys_cache = false /* will never be physical CD access */;
|
||||
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
|
||||
while(Running)
|
||||
{
|
||||
CDIF_Message msg;
|
||||
|
||||
// Only do a blocking-wait for a message if we don't have any sectors to read-ahead.
|
||||
// MDFN_DispMessage("%d %d %d\n", last_read_lba, ra_lba, ra_count);
|
||||
if(ReadThreadQueue.Read(&msg, ra_count ? FALSE : TRUE))
|
||||
{
|
||||
switch(msg.message)
|
||||
{
|
||||
case CDIF_MSG_DIEDIEDIE:
|
||||
Running = FALSE;
|
||||
break;
|
||||
|
||||
case CDIF_MSG_EJECT:
|
||||
RT_EjectDisc(msg.args[0]);
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
break;
|
||||
|
||||
case CDIF_MSG_READ_SECTOR:
|
||||
{
|
||||
static const int max_ra = 16;
|
||||
static const int initial_ra = 1;
|
||||
static const int speedmult_ra = 2;
|
||||
uint32 new_lba = msg.args[0];
|
||||
|
||||
if(last_read_lba != ~0U && new_lba == (last_read_lba + 1))
|
||||
{
|
||||
int how_far_ahead = ra_lba - new_lba;
|
||||
|
||||
if(how_far_ahead <= max_ra)
|
||||
ra_count = std::min(speedmult_ra, 1 + max_ra - how_far_ahead);
|
||||
else
|
||||
ra_count++;
|
||||
}
|
||||
else if(new_lba != last_read_lba)
|
||||
{
|
||||
ra_lba = new_lba;
|
||||
ra_count = initial_ra;
|
||||
}
|
||||
|
||||
last_read_lba = new_lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't read >= the "end" of the disc, silly snake. Slither.
|
||||
if(ra_count && ra_lba == disc_toc.tracks[100].lba)
|
||||
{
|
||||
ra_count = 0;
|
||||
//printf("Ephemeral scarabs: %d!\n", ra_lba);
|
||||
}
|
||||
|
||||
if(ra_count)
|
||||
{
|
||||
uint8 tmpbuf[2352 + 96];
|
||||
bool error_condition = false;
|
||||
|
||||
disc_cdaccess->Read_Raw_Sector(tmpbuf, ra_lba);
|
||||
|
||||
MDFND_LockMutex(SBMutex);
|
||||
|
||||
SectorBuffers[SBWritePos].lba = ra_lba;
|
||||
memcpy(SectorBuffers[SBWritePos].data, tmpbuf, 2352 + 96);
|
||||
SectorBuffers[SBWritePos].valid = TRUE;
|
||||
SectorBuffers[SBWritePos].error = error_condition;
|
||||
SBWritePos = (SBWritePos + 1) % SBSize;
|
||||
|
||||
MDFND_UnlockMutex(SBMutex);
|
||||
|
||||
ra_lba++;
|
||||
ra_count--;
|
||||
}
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
CDIF_MT::CDIF_MT(CDAccess *cda) : disc_cdaccess(cda), CDReadThread(NULL), SBMutex(NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
CDIF_Message msg;
|
||||
RTS_Args s;
|
||||
|
||||
SBMutex = MDFND_CreateMutex();
|
||||
UnrecoverableError = false;
|
||||
|
||||
s.cdif_ptr = this;
|
||||
|
||||
CDReadThread = MDFND_CreateThread(ReadThreadStart_C, &s);
|
||||
EmuThreadQueue.Read(&msg);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
if(CDReadThread)
|
||||
{
|
||||
MDFND_WaitThread(CDReadThread, NULL);
|
||||
CDReadThread = NULL;
|
||||
}
|
||||
|
||||
if(SBMutex)
|
||||
{
|
||||
MDFND_DestroyMutex(SBMutex);
|
||||
SBMutex = NULL;
|
||||
}
|
||||
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CDIF_MT::~CDIF_MT()
|
||||
{
|
||||
bool thread_murdered_with_kitchen_knife = false;
|
||||
|
||||
try
|
||||
{
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_DIEDIEDIE));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFND_PrintError(e.what());
|
||||
MDFND_KillThread(CDReadThread);
|
||||
thread_murdered_with_kitchen_knife = true;
|
||||
}
|
||||
|
||||
if(!thread_murdered_with_kitchen_knife)
|
||||
MDFND_WaitThread(CDReadThread, NULL);
|
||||
|
||||
if(SBMutex)
|
||||
{
|
||||
MDFND_DestroyMutex(SBMutex);
|
||||
SBMutex = NULL;
|
||||
}
|
||||
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDIF::ValidateRawSector(uint8 *buf)
|
||||
{
|
||||
int mode = buf[12 + 3];
|
||||
|
||||
if(mode != 0x1 && mode != 0x2)
|
||||
return(false);
|
||||
|
||||
if(!edc_lec_check_correct(buf, mode == 2))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool CDIF_MT::ReadRawSector(uint8 *buf, uint32 lba)
|
||||
{
|
||||
bool found = FALSE;
|
||||
bool error_condition = false;
|
||||
|
||||
if(UnrecoverableError)
|
||||
{
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
// This shouldn't happen, the emulated-system-specific CDROM emulation code should make sure the emulated program doesn't try
|
||||
// to read past the last "real" sector of the disc.
|
||||
if(lba >= disc_toc.tracks[100].lba)
|
||||
{
|
||||
printf("Attempt to read LBA %d, >= LBA %d\n", lba, disc_toc.tracks[100].lba);
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
|
||||
|
||||
do
|
||||
{
|
||||
MDFND_LockMutex(SBMutex);
|
||||
|
||||
for(int i = 0; i < SBSize; i++)
|
||||
{
|
||||
if(SectorBuffers[i].valid && SectorBuffers[i].lba == lba)
|
||||
{
|
||||
error_condition = SectorBuffers[i].error;
|
||||
memcpy(buf, SectorBuffers[i].data, 2352 + 96);
|
||||
found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
MDFND_UnlockMutex(SBMutex);
|
||||
|
||||
if(!found)
|
||||
MDFND_Sleep(1);
|
||||
} while(!found);
|
||||
|
||||
return(!error_condition);
|
||||
}
|
||||
|
||||
void CDIF_MT::HintReadSector(uint32 lba)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return;
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
|
||||
}
|
||||
|
||||
int CDIF::ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
while(nSectors--)
|
||||
{
|
||||
uint8 tmpbuf[2352 + 96];
|
||||
|
||||
if(!ReadRawSector(tmpbuf, lba))
|
||||
{
|
||||
puts("CDIF Raw Read error");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(!ValidateRawSector(tmpbuf))
|
||||
{
|
||||
MDFN_DispMessage(_("Uncorrectable data at sector %d"), lba);
|
||||
MDFN_PrintError(_("Uncorrectable data at sector %d"), lba);
|
||||
return(false);
|
||||
}
|
||||
|
||||
const int mode = tmpbuf[12 + 3];
|
||||
|
||||
if(!ret)
|
||||
ret = mode;
|
||||
|
||||
if(mode == 1)
|
||||
{
|
||||
memcpy(pBuf, &tmpbuf[12 + 4], 2048);
|
||||
}
|
||||
else if(mode == 2)
|
||||
{
|
||||
memcpy(pBuf, &tmpbuf[12 + 4 + 8], 2048);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba);
|
||||
return(false);
|
||||
}
|
||||
|
||||
pBuf += 2048;
|
||||
lba++;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool CDIF_MT::Eject(bool eject_status)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
try
|
||||
{
|
||||
CDIF_Message msg;
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_EJECT, eject_status));
|
||||
EmuThreadQueue.Read(&msg);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Error on eject/insert attempt: %s"), e.what());
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
class CDIF_Stream_Thing : public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 lba_arg, uint32 sector_count_arg);
|
||||
~CDIF_Stream_Thing();
|
||||
|
||||
virtual uint64 attributes(void);
|
||||
virtual uint8 *map(void);
|
||||
virtual void unmap(void);
|
||||
|
||||
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true);
|
||||
virtual void write(const void *data, uint64 count);
|
||||
|
||||
virtual void seek(int64 offset, int whence);
|
||||
virtual int64 tell(void);
|
||||
virtual int64 size(void);
|
||||
virtual void close(void);
|
||||
|
||||
private:
|
||||
CDIF *cdintf;
|
||||
const uint32 start_lba;
|
||||
const uint32 sector_count;
|
||||
int64 position;
|
||||
};
|
||||
|
||||
CDIF_Stream_Thing::CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 start_lba_arg, uint32 sector_count_arg) : cdintf(cdintf_arg), start_lba(start_lba_arg), sector_count(sector_count_arg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF_Stream_Thing::~CDIF_Stream_Thing()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64 CDIF_Stream_Thing::attributes(void)
|
||||
{
|
||||
return(ATTRIBUTE_READABLE | ATTRIBUTE_SEEKABLE);
|
||||
}
|
||||
|
||||
uint8 *CDIF_Stream_Thing::map(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::unmap(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
|
||||
{
|
||||
if(count > (((uint64)sector_count * 2048) - position))
|
||||
count = ((uint64)sector_count * 2048) - position;
|
||||
|
||||
if(!count)
|
||||
return(0);
|
||||
|
||||
for(uint64 rp = position; rp < (position + count); rp = (rp &~ 2047) + 2048)
|
||||
{
|
||||
uint8 buf[2048];
|
||||
|
||||
cdintf->ReadSector(buf, start_lba + (rp / 2048), 1);
|
||||
memcpy((uint8*)data + (rp - position), buf + (rp & 2047), std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
|
||||
}
|
||||
|
||||
position += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::write(const void *data, uint64 count)
|
||||
{
|
||||
throw MDFN_Error(ErrnoHolder(EBADF));
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::seek(int64 offset, int whence)
|
||||
{
|
||||
switch(whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
position = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
position += offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
position = ((int64)sector_count * 2048) + offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64 CDIF_Stream_Thing::tell(void)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
int64 CDIF_Stream_Thing::size(void)
|
||||
{
|
||||
return(sector_count * 2048);
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::close(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
Stream *CDIF::MakeStream(uint32 lba, uint32 sector_count)
|
||||
{
|
||||
return new CDIF_Stream_Thing(this, lba, sector_count);
|
||||
}
|
||||
|
||||
|
||||
CDIF *CDIF_Open(const char *path, bool image_memcache)
|
||||
{
|
||||
CDAccess *cda = cdaccess_open(path, image_memcache);
|
||||
|
||||
return new CDIF_MT(cda);
|
||||
}
|
159
mednafen/cdrom-0928/cdromif.h
Normal file
159
mednafen/cdrom-0928/cdromif.h
Normal file
@ -0,0 +1,159 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_CDROM_CDROMIF_H
|
||||
#define __MDFN_CDROM_CDROMIF_H
|
||||
|
||||
#include "CDUtility.h"
|
||||
#include "../Stream.h"
|
||||
#include "CDAccess.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
typedef CDUtility::TOC CD_TOC;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool valid;
|
||||
bool error;
|
||||
uint32 lba;
|
||||
uint8 data[2352 + 96];
|
||||
} CDIF_Sector_Buffer;
|
||||
|
||||
class CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF();
|
||||
virtual ~CDIF();
|
||||
|
||||
inline void ReadTOC(CDUtility::TOC *read_target)
|
||||
{
|
||||
*read_target = disc_toc;
|
||||
}
|
||||
|
||||
virtual void HintReadSector(uint32 lba) = 0;
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba) = 0;
|
||||
|
||||
// Call for mode 1 or mode 2 form 1 only.
|
||||
bool ValidateRawSector(uint8 *buf);
|
||||
|
||||
// Utility/Wrapped functions
|
||||
// Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned)
|
||||
// Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error
|
||||
int ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status) = 0;
|
||||
|
||||
inline bool IsPhysical(void) { return(is_phys_cache); }
|
||||
|
||||
// For Mode 1, or Mode 2 Form 1.
|
||||
// No reference counting or whatever is done, so if you destroy the CDIF object before you destroy the returned Stream, things will go BOOM.
|
||||
Stream *MakeStream(uint32 lba, uint32 sector_count);
|
||||
|
||||
protected:
|
||||
bool UnrecoverableError;
|
||||
bool is_phys_cache;
|
||||
CDUtility::TOC disc_toc;
|
||||
bool DiscEjected;
|
||||
};
|
||||
|
||||
class CDIF_Message
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Message();
|
||||
CDIF_Message(unsigned int message_, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0, uint32 arg3 = 0);
|
||||
CDIF_Message(unsigned int message_, const std::string &str);
|
||||
~CDIF_Message();
|
||||
|
||||
unsigned int message;
|
||||
uint32 args[4];
|
||||
void *parg;
|
||||
std::string str_message;
|
||||
};
|
||||
|
||||
class CDIF_Queue
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Queue();
|
||||
~CDIF_Queue();
|
||||
|
||||
bool Read(CDIF_Message *message, bool blocking = TRUE);
|
||||
|
||||
void Write(const CDIF_Message &message);
|
||||
|
||||
private:
|
||||
std::queue<CDIF_Message> ze_queue;
|
||||
MDFN_Mutex *ze_mutex;
|
||||
};
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_MT : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_MT(CDAccess *cda);
|
||||
virtual ~CDIF_MT();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
// FIXME: Semi-private:
|
||||
int ReadThreadStart(void);
|
||||
|
||||
private:
|
||||
|
||||
CDAccess *disc_cdaccess;
|
||||
|
||||
MDFN_Thread *CDReadThread;
|
||||
|
||||
// Queue for messages to the read thread.
|
||||
CDIF_Queue ReadThreadQueue;
|
||||
|
||||
// Queue for messages to the emu thread.
|
||||
CDIF_Queue EmuThreadQueue;
|
||||
|
||||
|
||||
enum { SBSize = 256 };
|
||||
CDIF_Sector_Buffer SectorBuffers[SBSize];
|
||||
|
||||
uint32 SBWritePos;
|
||||
|
||||
MDFN_Mutex *SBMutex;
|
||||
|
||||
|
||||
//
|
||||
// Read-thread-only:
|
||||
//
|
||||
void RT_EjectDisc(bool eject_status, bool skip_actual_eject = false);
|
||||
|
||||
uint32 ra_lba;
|
||||
int ra_count;
|
||||
uint32 last_read_lba;
|
||||
};
|
||||
|
||||
CDIF *CDIF_Open(const char *path, bool image_memcache);
|
||||
|
||||
#endif
|
170
mednafen/cdrom-0928/dvdisaster.h
Normal file
170
mednafen/cdrom-0928/dvdisaster.h
Normal file
@ -0,0 +1,170 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#ifndef DVDISASTER_H
|
||||
#define DVDISASTER_H
|
||||
|
||||
/* "Dare to be gorgeous and unique.
|
||||
* But don't ever be cryptic or otherwise unfathomable.
|
||||
* Make it unforgettably great."
|
||||
*
|
||||
* From "A Final Note on Style",
|
||||
* Amiga Intuition Reference Manual, 1986, p. 231
|
||||
*/
|
||||
|
||||
/***
|
||||
*** I'm too lazy to mess with #include dependencies.
|
||||
*** Everything #includeable is rolled up herein...
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen-types.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/***
|
||||
*** dvdisaster.c
|
||||
***/
|
||||
|
||||
void PrepareDeadSector(void);
|
||||
|
||||
void CreateEcc(void);
|
||||
void FixEcc(void);
|
||||
void Verify(void);
|
||||
|
||||
uint32 EDCCrc32(const unsigned char*, int);
|
||||
|
||||
/***
|
||||
*** galois.c
|
||||
***
|
||||
* This is currently the hardcoded GF(2**8).
|
||||
* int32 gives abundant space for the GF.
|
||||
* Squeezing it down to uint8 won't probably gain much,
|
||||
* so we implement this defensively here.
|
||||
*
|
||||
* Note that some performance critical stuff needs to
|
||||
* be #included from galois-inlines.h
|
||||
*/
|
||||
|
||||
/* Galois field parameters for 8bit symbol Reed-Solomon code */
|
||||
|
||||
#define GF_SYMBOLSIZE 8
|
||||
#define GF_FIELDSIZE (1<<GF_SYMBOLSIZE)
|
||||
#define GF_FIELDMAX (GF_FIELDSIZE-1)
|
||||
#define GF_ALPHA0 GF_FIELDMAX
|
||||
|
||||
/* Lookup tables for Galois field arithmetic */
|
||||
|
||||
typedef struct _GaloisTables
|
||||
{ int32 gfGenerator; /* GF generator polynomial */
|
||||
int32 *indexOf; /* log */
|
||||
int32 *alphaTo; /* inverse log */
|
||||
int32 *encAlphaTo; /* inverse log optimized for encoder */
|
||||
} GaloisTables;
|
||||
|
||||
/* Lookup and working tables for the ReedSolomon codecs */
|
||||
|
||||
typedef struct _ReedSolomonTables
|
||||
{ GaloisTables *gfTables;/* from above */
|
||||
int32 *gpoly; /* RS code generator polynomial */
|
||||
int32 fcr; /* first consecutive root of RS generator polynomial */
|
||||
int32 primElem; /* primitive field element */
|
||||
int32 nroots; /* degree of RS generator polynomial */
|
||||
int32 ndata; /* data bytes per ecc block */
|
||||
} ReedSolomonTables;
|
||||
|
||||
GaloisTables* CreateGaloisTables(int32);
|
||||
void FreeGaloisTables(GaloisTables*);
|
||||
|
||||
ReedSolomonTables *CreateReedSolomonTables(GaloisTables*, int32, int32, int);
|
||||
void FreeReedSolomonTables(ReedSolomonTables*);
|
||||
|
||||
/***
|
||||
*** l-ec.c
|
||||
***/
|
||||
|
||||
#define N_P_VECTORS 86 /* 43 16bit p vectors */
|
||||
#define P_VECTOR_SIZE 26 /* using RS(26,24) ECC */
|
||||
|
||||
#define N_Q_VECTORS 52 /* 26 16bit q vectors */
|
||||
#define Q_VECTOR_SIZE 45 /* using RS(45,43) ECC */
|
||||
|
||||
#define P_PADDING 229 /* padding values for */
|
||||
#define Q_PADDING 210 /* shortened RS code */
|
||||
|
||||
int PToByteIndex(int, int);
|
||||
int QToByteIndex(int, int);
|
||||
void ByteIndexToP(int, int*, int*);
|
||||
void ByteIndexToQ(int, int*, int*);
|
||||
|
||||
void GetPVector(unsigned char*, unsigned char*, int);
|
||||
void SetPVector(unsigned char*, unsigned char*, int);
|
||||
void FillPVector(unsigned char*, unsigned char, int);
|
||||
void AndPVector(unsigned char*, unsigned char, int);
|
||||
void OrPVector(unsigned char*, unsigned char, int);
|
||||
|
||||
void GetQVector(unsigned char*, unsigned char*, int);
|
||||
void SetQVector(unsigned char*, unsigned char*, int);
|
||||
void FillQVector(unsigned char*, unsigned char, int);
|
||||
void AndQVector(unsigned char*, unsigned char, int);
|
||||
void OrQVector(unsigned char*, unsigned char, int);
|
||||
|
||||
int DecodePQ(ReedSolomonTables*, unsigned char*, int, int*, int);
|
||||
|
||||
int CountC2Errors(unsigned char*);
|
||||
|
||||
/***
|
||||
*** misc.c
|
||||
***/
|
||||
|
||||
char* sgettext(char*);
|
||||
char* sgettext_utf8(char*);
|
||||
|
||||
int64 uchar_to_int64(unsigned char*);
|
||||
void int64_to_uchar(unsigned char*, int64);
|
||||
|
||||
void CalcSectors(int64, int64*, int*);
|
||||
|
||||
/***
|
||||
*** recover-raw.c
|
||||
***/
|
||||
|
||||
#define CD_RAW_SECTOR_SIZE 2352
|
||||
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
|
||||
|
||||
int CheckEDC(const unsigned char*, bool);
|
||||
int CheckMSF(unsigned char*, int);
|
||||
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode);
|
||||
bool Init_LEC_Correct(void);
|
||||
void Kill_LEC_Correct(void);
|
||||
|
||||
|
||||
#endif /* DVDISASTER_H */
|
40
mednafen/cdrom-0928/galois-inlines.h
Normal file
40
mednafen/cdrom-0928/galois-inlines.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
/*
|
||||
* The following routine is performance critical.
|
||||
*/
|
||||
|
||||
static inline int mod_fieldmax(int x)
|
||||
{
|
||||
while (x >= GF_FIELDMAX)
|
||||
{
|
||||
x -= GF_FIELDMAX;
|
||||
x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
156
mednafen/cdrom-0928/galois.cpp
Normal file
156
mednafen/cdrom-0928/galois.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "galois-inlines.h"
|
||||
|
||||
/***
|
||||
*** Galois field arithmetic.
|
||||
***
|
||||
* Calculations are done over the extension field GF(2**n).
|
||||
* Be careful not to overgeneralize these arithmetics;
|
||||
* they only work for the case of GF(p**n) with p being prime.
|
||||
*/
|
||||
|
||||
/* Initialize the Galois field tables */
|
||||
|
||||
|
||||
GaloisTables* CreateGaloisTables(int32 gf_generator)
|
||||
{
|
||||
GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables));
|
||||
int32 b,log;
|
||||
|
||||
/* Allocate the tables.
|
||||
The encoder uses a special version of alpha_to which has the mod_fieldmax()
|
||||
folded into the table. */
|
||||
|
||||
gt->gfGenerator = gf_generator;
|
||||
|
||||
gt->indexOf = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||
gt->alphaTo = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||
gt->encAlphaTo = (int32 *)calloc(2*GF_FIELDSIZE, sizeof(int32));
|
||||
|
||||
/* create the log/ilog values */
|
||||
|
||||
for(b=1, log=0; log<GF_FIELDMAX; log++)
|
||||
{ gt->indexOf[b] = log;
|
||||
gt->alphaTo[log] = b;
|
||||
b = b << 1;
|
||||
if(b & GF_FIELDSIZE)
|
||||
b = b ^ gf_generator;
|
||||
}
|
||||
|
||||
if(b!=1)
|
||||
{
|
||||
printf("Failed to create the Galois field log tables!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* we're even closed using infinity (makes things easier) */
|
||||
|
||||
gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */
|
||||
gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */
|
||||
|
||||
for(b=0; b<2*GF_FIELDSIZE; b++)
|
||||
gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)];
|
||||
|
||||
return gt;
|
||||
}
|
||||
|
||||
void FreeGaloisTables(GaloisTables *gt)
|
||||
{
|
||||
if(gt->indexOf) free(gt->indexOf);
|
||||
if(gt->alphaTo) free(gt->alphaTo);
|
||||
if(gt->encAlphaTo) free(gt->encAlphaTo);
|
||||
|
||||
free(gt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** Create the the Reed-Solomon generator polynomial
|
||||
*** and some auxiliary data structures.
|
||||
*/
|
||||
|
||||
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
|
||||
int32 first_consecutive_root,
|
||||
int32 prim_elem,
|
||||
int nroots_in)
|
||||
{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables));
|
||||
int32 i,j,root;
|
||||
|
||||
rt->gfTables = gt;
|
||||
rt->fcr = first_consecutive_root;
|
||||
rt->primElem = prim_elem;
|
||||
rt->nroots = nroots_in;
|
||||
rt->ndata = GF_FIELDMAX - rt->nroots;
|
||||
|
||||
rt->gpoly = (int32 *)calloc((rt->nroots+1), sizeof(int32));
|
||||
|
||||
/* Create the RS code generator polynomial */
|
||||
|
||||
rt->gpoly[0] = 1;
|
||||
|
||||
for(i=0, root=first_consecutive_root*prim_elem; i<rt->nroots; i++, root+=prim_elem)
|
||||
{ rt->gpoly[i+1] = 1;
|
||||
|
||||
/* Multiply gpoly by alpha**(root+x) */
|
||||
|
||||
for(j=i; j>0; j--)
|
||||
{
|
||||
if(rt->gpoly[j] != 0)
|
||||
rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)];
|
||||
else
|
||||
rt->gpoly[j] = rt->gpoly[j-1];
|
||||
}
|
||||
|
||||
rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)];
|
||||
}
|
||||
|
||||
/* Store the polynomials index for faster encoding */
|
||||
|
||||
for(i=0; i<=rt->nroots; i++)
|
||||
rt->gpoly[i] = gt->indexOf[rt->gpoly[i]];
|
||||
|
||||
#if 0
|
||||
/* for the precalculated unrolled loops only */
|
||||
|
||||
for(i=gt->nroots-1; i>0; i--)
|
||||
PrintCLI(
|
||||
" par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n",
|
||||
nroots-1,gt->gpoly[i]);
|
||||
|
||||
PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n",
|
||||
gt->gpoly[0]);
|
||||
#endif
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
void FreeReedSolomonTables(ReedSolomonTables *rt)
|
||||
{
|
||||
if(rt->gpoly) free(rt->gpoly);
|
||||
|
||||
free(rt);
|
||||
}
|
478
mednafen/cdrom-0928/l-ec.cpp
Normal file
478
mednafen/cdrom-0928/l-ec.cpp
Normal file
@ -0,0 +1,478 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "galois-inlines.h"
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/***
|
||||
*** Mapping between cd frame and parity vectors
|
||||
***/
|
||||
|
||||
/*
|
||||
* Mapping of frame bytes to P/Q Vectors
|
||||
*/
|
||||
|
||||
int PToByteIndex(int p, int i)
|
||||
{ return 12 + p + i*86;
|
||||
}
|
||||
|
||||
void ByteIndexToP(int b, int *p, int *i)
|
||||
{ *p = (b-12)%86;
|
||||
*i = (b-12)/86;
|
||||
}
|
||||
|
||||
int QToByteIndex(int q, int i)
|
||||
{ int offset = 12 + (q & 1);
|
||||
|
||||
if(i == 43) return 2248+q;
|
||||
if(i == 44) return 2300+q;
|
||||
|
||||
q&=~1;
|
||||
return offset + (q*43 + i*88) % 2236;
|
||||
}
|
||||
|
||||
void ByteIndexToQ(int b, int *q, int *i)
|
||||
{ int x,y,offset;
|
||||
|
||||
if(b >= 2300)
|
||||
{ *i = 44;
|
||||
*q = (b-2300);
|
||||
return;
|
||||
}
|
||||
|
||||
if(b >= 2248)
|
||||
{ *i = 43;
|
||||
*q = (b-2248);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = b&1;
|
||||
b = (b-12)/2;
|
||||
x = b/43;
|
||||
y = (b-(x*43))%26;
|
||||
*i = b-(x*43);
|
||||
*q = 2*((x+26-y)%26)+offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 86 vectors of P-parity, yielding a RS(26,24) code.
|
||||
*/
|
||||
|
||||
void GetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
data[i] = frame[w_idx];
|
||||
}
|
||||
|
||||
void SetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] = data[i];
|
||||
}
|
||||
|
||||
void FillPVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] = data;
|
||||
}
|
||||
|
||||
void OrPVector(unsigned char *frame, unsigned char value, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] |= value;
|
||||
}
|
||||
|
||||
void AndPVector(unsigned char *frame, unsigned char value, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] &= value;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 52 vectors of Q-parity, yielding a RS(45,43) code.
|
||||
*/
|
||||
|
||||
void GetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
data[i] = frame[(w_idx % 2236) + offset];
|
||||
|
||||
data[43] = frame[2248 + n];
|
||||
data[44] = frame[2300 + n];
|
||||
}
|
||||
|
||||
void SetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] = data[i];
|
||||
|
||||
frame[2248 + n] = data[43];
|
||||
frame[2300 + n] = data[44];
|
||||
}
|
||||
|
||||
void FillQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] = data;
|
||||
|
||||
frame[2248 + n] = data;
|
||||
frame[2300 + n] = data;
|
||||
}
|
||||
|
||||
void OrQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] |= data;
|
||||
|
||||
frame[2248 + n] |= data;
|
||||
frame[2300 + n] |= data;
|
||||
}
|
||||
|
||||
void AndQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] &= data;
|
||||
|
||||
frame[2248 + n] &= data;
|
||||
frame[2300 + n] &= data;
|
||||
}
|
||||
|
||||
/***
|
||||
*** C2 error counting
|
||||
***/
|
||||
|
||||
int CountC2Errors(unsigned char *frame)
|
||||
{ int i,count = 0;
|
||||
frame += 2352;
|
||||
|
||||
for(i=0; i<294; i++, frame++)
|
||||
{ if(*frame & 0x01) count++;
|
||||
if(*frame & 0x02) count++;
|
||||
if(*frame & 0x04) count++;
|
||||
if(*frame & 0x08) count++;
|
||||
if(*frame & 0x10) count++;
|
||||
if(*frame & 0x20) count++;
|
||||
if(*frame & 0x40) count++;
|
||||
if(*frame & 0x80) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/***
|
||||
*** L-EC error correction for CD raw data sectors
|
||||
***/
|
||||
|
||||
/*
|
||||
* These could be used from ReedSolomonTables,
|
||||
* but hardcoding them is faster.
|
||||
*/
|
||||
|
||||
#define NROOTS 2
|
||||
#define LEC_FIRST_ROOT 0 //GF_ALPHA0
|
||||
#define LEC_PRIM_ELEM 1
|
||||
#define LEC_PRIMTH_ROOT 1
|
||||
|
||||
/*
|
||||
* Calculate the error syndrome
|
||||
*/
|
||||
|
||||
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
|
||||
int *erasure_list, int erasure_count)
|
||||
{ GaloisTables *gt = rt->gfTables;
|
||||
int syndrome[NROOTS];
|
||||
int lambda[NROOTS+1];
|
||||
int omega[NROOTS+1];
|
||||
int b[NROOTS+1];
|
||||
int reg[NROOTS+1];
|
||||
int root[NROOTS];
|
||||
int loc[NROOTS];
|
||||
int syn_error;
|
||||
int deg_lambda,lambda_roots;
|
||||
int deg_omega;
|
||||
int shortened_size = GF_FIELDMAX - padding;
|
||||
int corrected = 0;
|
||||
int i,j,k;
|
||||
int r,el;
|
||||
|
||||
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||
|
||||
for(i=0; i<NROOTS; i++)
|
||||
syndrome[i] = data[0];
|
||||
|
||||
for(j=1; j<shortened_size; j++)
|
||||
for(i=0; i<NROOTS; i++)
|
||||
if(syndrome[i] == 0)
|
||||
syndrome[i] = data[j];
|
||||
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||
|
||||
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||
|
||||
syn_error = 0;
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ syn_error |= syndrome[i];
|
||||
syndrome[i] = gt->indexOf[syndrome[i]];
|
||||
}
|
||||
|
||||
/*** If the syndrome is zero, everything is fine. */
|
||||
|
||||
if(!syn_error)
|
||||
return 0;
|
||||
|
||||
/*** Initialize lambda to be the erasure locator polynomial */
|
||||
|
||||
lambda[0] = 1;
|
||||
lambda[1] = lambda[2] = 0;
|
||||
|
||||
erasure_list[0] += padding;
|
||||
erasure_list[1] += padding;
|
||||
|
||||
if(erasure_count > 2) /* sanity check */
|
||||
erasure_count = 0;
|
||||
|
||||
if(erasure_count > 0)
|
||||
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
|
||||
|
||||
for(i=1; i<erasure_count; i++)
|
||||
{ int u = mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
|
||||
|
||||
for(j=i+1; j>0; j--)
|
||||
{ int tmp = gt->indexOf[lambda[j-1]];
|
||||
|
||||
if(tmp != GF_ALPHA0)
|
||||
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<NROOTS+1; i++)
|
||||
b[i] = gt->indexOf[lambda[i]];
|
||||
|
||||
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
|
||||
|
||||
r = erasure_count; /* r is the step number */
|
||||
el = erasure_count;
|
||||
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
|
||||
while(++r <= NROOTS)
|
||||
{ int discr_r = 0;
|
||||
|
||||
for(i=0; i<r; i++)
|
||||
if((lambda[i] != 0) && (syndrome[r-i-1] != GF_ALPHA0))
|
||||
discr_r ^= gt->alphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])];
|
||||
|
||||
discr_r = gt->indexOf[discr_r];
|
||||
|
||||
if(discr_r == GF_ALPHA0)
|
||||
{ /* B(x) = x*B(x) */
|
||||
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||
b[0] = GF_ALPHA0;
|
||||
}
|
||||
else
|
||||
{ int t[NROOTS+1];
|
||||
|
||||
/* T(x) = lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ if(b[i] != GF_ALPHA0)
|
||||
t[i+1] = lambda[i+1] ^ gt->alphaTo[mod_fieldmax(discr_r + b[i])];
|
||||
else t[i+1] = lambda[i+1];
|
||||
}
|
||||
|
||||
if(2*el <= r+erasure_count-1)
|
||||
{ el = r + erasure_count - el;
|
||||
|
||||
/* B(x) <-- inv(discr_r) * lambda(x) */
|
||||
for(i=0; i<=NROOTS; i++)
|
||||
b[i] = (lambda[i] == 0) ? GF_ALPHA0
|
||||
: mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX);
|
||||
}
|
||||
else
|
||||
{ /* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||
b[0] = GF_ALPHA0;
|
||||
}
|
||||
|
||||
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/*** Convert lambda to index form and compute deg(lambda(x)) */
|
||||
|
||||
deg_lambda = 0;
|
||||
for(i=0; i<NROOTS+1; i++)
|
||||
{ lambda[i] = gt->indexOf[lambda[i]];
|
||||
if(lambda[i] != GF_ALPHA0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
|
||||
/*** Find roots of the error+erasure locator polynomial by Chien search */
|
||||
|
||||
memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0]));
|
||||
lambda_roots = 0; /* Number of roots of lambda(x) */
|
||||
|
||||
for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT))
|
||||
{ int q=1; /* lambda[0] is always 0 */
|
||||
|
||||
for(j=deg_lambda; j>0; j--)
|
||||
{ if(reg[j] != GF_ALPHA0)
|
||||
{ reg[j] = mod_fieldmax(reg[j] + j);
|
||||
q ^= gt->alphaTo[reg[j]];
|
||||
}
|
||||
}
|
||||
|
||||
if(q != 0) continue; /* Not a root */
|
||||
|
||||
/* store root in index-form and the error location number */
|
||||
|
||||
root[lambda_roots] = i;
|
||||
loc[lambda_roots] = k;
|
||||
|
||||
/* If we've already found max possible roots, abort the search to save time */
|
||||
|
||||
if(++lambda_roots == deg_lambda) break;
|
||||
}
|
||||
|
||||
/* deg(lambda) unequal to number of roots => uncorrectable error detected
|
||||
This is not reliable for very small numbers of roots, e.g. nroots = 2 */
|
||||
|
||||
if(deg_lambda != lambda_roots)
|
||||
{ return -1;
|
||||
}
|
||||
|
||||
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
|
||||
(modulo x**nroots). in index form. Also find deg(omega). */
|
||||
|
||||
deg_omega = deg_lambda-1;
|
||||
|
||||
for(i=0; i<=deg_omega; i++)
|
||||
{ int tmp = 0;
|
||||
|
||||
for(j=i; j>=0; j--)
|
||||
{ if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
|
||||
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
|
||||
}
|
||||
|
||||
omega[i] = gt->indexOf[tmp];
|
||||
}
|
||||
|
||||
/* Compute error values in poly-form.
|
||||
num1 = omega(inv(X(l))),
|
||||
num2 = inv(X(l))**(FIRST_ROOT-1) and
|
||||
den = lambda_pr(inv(X(l))) all in poly-form. */
|
||||
|
||||
for(j=lambda_roots-1; j>=0; j--)
|
||||
{ int num1 = 0;
|
||||
int num2;
|
||||
int den;
|
||||
int location = loc[j];
|
||||
|
||||
for(i=deg_omega; i>=0; i--)
|
||||
{ if(omega[i] != GF_ALPHA0)
|
||||
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
|
||||
}
|
||||
|
||||
num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
|
||||
for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2)
|
||||
{ if(lambda[i+1] != GF_ALPHA0)
|
||||
den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
|
||||
/* Apply error to data */
|
||||
|
||||
if(num1 != 0 && location >= padding)
|
||||
{
|
||||
corrected++;
|
||||
data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2]
|
||||
+ GF_FIELDMAX - gt->indexOf[den])];
|
||||
|
||||
/* If no erasures were given, at most one error was corrected.
|
||||
Return its position in erasure_list[0]. */
|
||||
|
||||
if(!erasure_count)
|
||||
erasure_list[0] = location-padding;
|
||||
}
|
||||
#if 1
|
||||
else return -3;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||
|
||||
for(i=0; i<NROOTS; i++)
|
||||
syndrome[i] = data[0];
|
||||
|
||||
for(j=1; j<shortened_size; j++)
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ if(syndrome[i] == 0)
|
||||
syndrome[i] = data[j];
|
||||
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||
}
|
||||
|
||||
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||
#if 1
|
||||
for(i=0; i<NROOTS; i++)
|
||||
if(syndrome[i])
|
||||
return -2;
|
||||
#endif
|
||||
|
||||
return corrected;
|
||||
}
|
||||
|
||||
|
594
mednafen/cdrom-0928/lec.cpp
Normal file
594
mednafen/cdrom-0928/lec.cpp
Normal file
@ -0,0 +1,594 @@
|
||||
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||
*
|
||||
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "lec.h"
|
||||
|
||||
#define GF8_PRIM_POLY 0x11d /* x^8 + x^4 + x^3 + x^2 + 1 */
|
||||
|
||||
#define EDC_POLY 0x8001801b /* (x^16 + x^15 + x^2 + 1) (x^16 + x^2 + x + 1) */
|
||||
|
||||
#define LEC_HEADER_OFFSET 12
|
||||
#define LEC_DATA_OFFSET 16
|
||||
#define LEC_MODE1_DATA_LEN 2048
|
||||
#define LEC_MODE1_EDC_OFFSET 2064
|
||||
#define LEC_MODE1_INTERMEDIATE_OFFSET 2068
|
||||
#define LEC_MODE1_P_PARITY_OFFSET 2076
|
||||
#define LEC_MODE1_Q_PARITY_OFFSET 2248
|
||||
#define LEC_MODE2_FORM1_DATA_LEN (2048+8)
|
||||
#define LEC_MODE2_FORM1_EDC_OFFSET 2072
|
||||
#define LEC_MODE2_FORM2_DATA_LEN (2324+8)
|
||||
#define LEC_MODE2_FORM2_EDC_OFFSET 2348
|
||||
|
||||
|
||||
typedef uint8_t gf8_t;
|
||||
|
||||
static uint8_t GF8_LOG[256];
|
||||
static gf8_t GF8_ILOG[256];
|
||||
|
||||
static const class Gf8_Q_Coeffs_Results_01 {
|
||||
private:
|
||||
uint16_t table[43][256];
|
||||
public:
|
||||
Gf8_Q_Coeffs_Results_01();
|
||||
~Gf8_Q_Coeffs_Results_01() {}
|
||||
const uint16_t *operator[] (int i) const { return &table[i][0]; }
|
||||
operator const uint16_t *() const { return &table[0][0]; }
|
||||
} CF8_Q_COEFFS_RESULTS_01;
|
||||
|
||||
static const class CrcTable {
|
||||
private:
|
||||
uint32_t table[256];
|
||||
public:
|
||||
CrcTable();
|
||||
~CrcTable() {}
|
||||
uint32_t operator[](int i) const { return table[i]; }
|
||||
operator const uint32_t *() const { return table; }
|
||||
} CRCTABLE;
|
||||
|
||||
static const class ScrambleTable {
|
||||
private:
|
||||
uint8_t table[2340];
|
||||
public:
|
||||
ScrambleTable();
|
||||
~ScrambleTable() {}
|
||||
uint8_t operator[](int i) const { return table[i]; }
|
||||
operator const uint8_t *() const { return table; }
|
||||
} SCRAMBLE_TABLE;
|
||||
|
||||
/* Creates the logarithm and inverse logarithm table that is required
|
||||
* for performing multiplication in the GF(8) domain.
|
||||
*/
|
||||
static void gf8_create_log_tables()
|
||||
{
|
||||
uint8_t log;
|
||||
uint16_t b;
|
||||
|
||||
for (b = 0; b <= 255; b++) {
|
||||
GF8_LOG[b] = 0;
|
||||
GF8_ILOG[b] = 0;
|
||||
}
|
||||
|
||||
b = 1;
|
||||
|
||||
for (log = 0; log < 255; log++) {
|
||||
GF8_LOG[(uint8_t)b] = log;
|
||||
GF8_ILOG[log] = (uint8_t)b;
|
||||
|
||||
b <<= 1;
|
||||
|
||||
if ((b & 0x100) != 0)
|
||||
b ^= GF8_PRIM_POLY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Addition in the GF(8) domain: just the XOR of the values.
|
||||
*/
|
||||
#define gf8_add(a, b) (a) ^ (b)
|
||||
|
||||
|
||||
/* Multiplication in the GF(8) domain: add the logarithms (modulo 255)
|
||||
* and return the inverse logarithm. Not used!
|
||||
*/
|
||||
#if 0
|
||||
static gf8_t gf8_mult(gf8_t a, gf8_t b)
|
||||
{
|
||||
int16_t sum;
|
||||
|
||||
if (a == 0 || b == 0)
|
||||
return 0;
|
||||
|
||||
sum = GF8_LOG[a] + GF8_LOG[b];
|
||||
|
||||
if (sum >= 255)
|
||||
sum -= 255;
|
||||
|
||||
return GF8_ILOG[sum];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Division in the GF(8) domain: Like multiplication but logarithms a
|
||||
* subtracted.
|
||||
*/
|
||||
static gf8_t gf8_div(gf8_t a, gf8_t b)
|
||||
{
|
||||
int16_t sum;
|
||||
|
||||
assert(b != 0);
|
||||
|
||||
if (a == 0)
|
||||
return 0;
|
||||
|
||||
sum = GF8_LOG[a] - GF8_LOG[b];
|
||||
|
||||
if (sum < 0)
|
||||
sum += 255;
|
||||
|
||||
return GF8_ILOG[sum];
|
||||
}
|
||||
|
||||
Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
|
||||
{
|
||||
int i, j;
|
||||
uint16_t c;
|
||||
gf8_t GF8_COEFFS_HELP[2][45];
|
||||
uint8_t GF8_Q_COEFFS[2][45];
|
||||
|
||||
|
||||
gf8_create_log_tables();
|
||||
|
||||
/* build matrix H:
|
||||
* 1 1 ... 1 1
|
||||
* a^44 a^43 ... a^1 a^0
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_COEFFS_HELP[0][j] = 1; /* e0 */
|
||||
GF8_COEFFS_HELP[1][j] = GF8_ILOG[44-j]; /* e1 */
|
||||
}
|
||||
|
||||
|
||||
/* resolve equation system for parity byte 0 and 1 */
|
||||
|
||||
/* e1' = e1 + e0 */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[1][j] = gf8_add(GF8_COEFFS_HELP[1][j],
|
||||
GF8_COEFFS_HELP[0][j]);
|
||||
}
|
||||
|
||||
/* e1'' = e1' / (a^1 + 1) */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[1][j] = gf8_div(GF8_Q_COEFFS[1][j], GF8_Q_COEFFS[1][43]);
|
||||
}
|
||||
|
||||
/* e0' = e0 + e1 / a^1 */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[0][j] = gf8_add(GF8_COEFFS_HELP[0][j],
|
||||
gf8_div(GF8_COEFFS_HELP[1][j],
|
||||
GF8_ILOG[1]));
|
||||
}
|
||||
|
||||
/* e0'' = e0' / (1 + 1 / a^1) */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[0][j] = gf8_div(GF8_Q_COEFFS[0][j], GF8_Q_COEFFS[0][44]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the products of 0..255 with all of the Q coefficients in
|
||||
* advance. When building the scalar product between the data vectors
|
||||
* and the P/Q vectors the individual products can be looked up in
|
||||
* this table
|
||||
*
|
||||
* The P parity coefficients are just a subset of the Q coefficients so
|
||||
* that we do not need to create a separate table for them.
|
||||
*/
|
||||
|
||||
for (j = 0; j < 43; j++) {
|
||||
|
||||
table[j][0] = 0;
|
||||
|
||||
for (i = 1; i < 256; i++) {
|
||||
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[0][j]];
|
||||
if (c >= 255) c -= 255;
|
||||
table[j][i] = GF8_ILOG[c];
|
||||
|
||||
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]];
|
||||
if (c >= 255) c -= 255;
|
||||
table[j][i] |= GF8_ILOG[c]<<8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
|
||||
*/
|
||||
static uint32_t mirror_bits(uint32_t d, int bits)
|
||||
{
|
||||
int i;
|
||||
uint32_t r = 0;
|
||||
|
||||
for (i = 0; i < bits; i++) {
|
||||
r <<= 1;
|
||||
|
||||
if ((d & 0x1) != 0)
|
||||
r |= 0x1;
|
||||
|
||||
d >>= 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Build the CRC lookup table for EDC_POLY poly. The CRC is 32 bit wide
|
||||
* and reversed (i.e. the bit stream is divided by the EDC_POLY with the
|
||||
* LSB first order).
|
||||
*/
|
||||
CrcTable::CrcTable ()
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint32_t r;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
r = mirror_bits(i, 8);
|
||||
|
||||
r <<= 24;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((r & 0x80000000) != 0) {
|
||||
r <<= 1;
|
||||
r ^= EDC_POLY;
|
||||
}
|
||||
else {
|
||||
r <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
r = mirror_bits(r, 32);
|
||||
|
||||
table[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the CRC of given data with given lengths based on the
|
||||
* table lookup algorithm.
|
||||
*/
|
||||
static uint32_t calc_edc(uint8_t *data, int len)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Build the scramble table as defined in the yellow book. The bytes
|
||||
12 to 2351 of a sector will be XORed with the data of this table.
|
||||
*/
|
||||
ScrambleTable::ScrambleTable()
|
||||
{
|
||||
uint16_t i, j;
|
||||
uint16_t reg = 1;
|
||||
uint8_t d;
|
||||
|
||||
for (i = 0; i < 2340; i++) {
|
||||
d = 0;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
d >>= 1;
|
||||
|
||||
if ((reg & 0x1) != 0)
|
||||
d |= 0x80;
|
||||
|
||||
if ((reg & 0x1) != ((reg >> 1) & 0x1)) {
|
||||
reg >>= 1;
|
||||
reg |= 0x4000; /* 15-bit register */
|
||||
}
|
||||
else {
|
||||
reg >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
table[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calc EDC for a MODE 1 sector
|
||||
*/
|
||||
static void calc_mode1_edc(uint8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
|
||||
|
||||
sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Calc EDC for a XA form 1 sector
|
||||
*/
|
||||
static void calc_mode2_form1_edc(uint8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM1_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Calc EDC for a XA form 2 sector
|
||||
*/
|
||||
static void calc_mode2_form2_edc(uint8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM2_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Writes the sync pattern to the given sector.
|
||||
*/
|
||||
static void set_sync_pattern(uint8_t *sector)
|
||||
{
|
||||
sector[0] = 0;
|
||||
|
||||
sector[1] = sector[2] = sector[3] = sector[4] = sector[5] =
|
||||
sector[6] = sector[7] = sector[8] = sector[9] = sector[10] = 0xff;
|
||||
|
||||
sector[11] = 0;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t bin2bcd(uint8_t b)
|
||||
{
|
||||
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
|
||||
}
|
||||
|
||||
/* Builds the sector header.
|
||||
*/
|
||||
static void set_sector_header(uint8_t mode, uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75));
|
||||
sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60);
|
||||
sector[LEC_HEADER_OFFSET + 2] = bin2bcd(adr % 75);
|
||||
sector[LEC_HEADER_OFFSET + 3] = mode;
|
||||
}
|
||||
|
||||
/* Calculate the P parities for the sector.
|
||||
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
|
||||
*/
|
||||
static void calc_P_parity(uint8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t p01_msb, p01_lsb;
|
||||
uint8_t *p_lsb_start;
|
||||
uint8_t *p_lsb;
|
||||
uint8_t *p0, *p1;
|
||||
uint8_t d0,d1;
|
||||
|
||||
p_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
|
||||
p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
|
||||
|
||||
for (i = 0; i <= 42; i++) {
|
||||
p_lsb = p_lsb_start;
|
||||
|
||||
p01_lsb = p01_msb = 0;
|
||||
|
||||
for (j = 19; j <= 42; j++) {
|
||||
d0 = *p_lsb;
|
||||
d1 = *(p_lsb+1);
|
||||
|
||||
p01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||
p01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||
|
||||
p_lsb += 2 * 43;
|
||||
}
|
||||
|
||||
*p0 = p01_lsb;
|
||||
*(p0 + 1) = p01_msb;
|
||||
|
||||
*p1 = p01_lsb>>8;
|
||||
*(p1 + 1) = p01_msb>>8;
|
||||
|
||||
p0 += 2;
|
||||
p1 += 2;
|
||||
|
||||
p_lsb_start += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the Q parities for the sector.
|
||||
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
|
||||
*/
|
||||
static void calc_Q_parity(uint8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t q01_lsb, q01_msb;
|
||||
uint8_t *q_lsb_start;
|
||||
uint8_t *q_lsb;
|
||||
uint8_t *q0, *q1, *q_start;
|
||||
uint8_t d0,d1;
|
||||
|
||||
q_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
|
||||
|
||||
for (i = 0; i <= 25; i++) {
|
||||
q_lsb = q_lsb_start;
|
||||
|
||||
q01_lsb = q01_msb = 0;
|
||||
|
||||
for (j = 0; j <= 42; j++) {
|
||||
d0 = *q_lsb;
|
||||
d1 = *(q_lsb+1);
|
||||
|
||||
q01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||
q01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||
|
||||
q_lsb += 2 * 44;
|
||||
|
||||
if (q_lsb >= q_start) {
|
||||
q_lsb -= 2 * 1118;
|
||||
}
|
||||
}
|
||||
|
||||
*q0 = q01_lsb;
|
||||
*(q0 + 1) = q01_msb;
|
||||
|
||||
*q1 = q01_lsb>>8;
|
||||
*(q1 + 1) = q01_msb>>8;
|
||||
|
||||
q0 += 2;
|
||||
q1 += 2;
|
||||
|
||||
q_lsb_start += 2 * 43;
|
||||
}
|
||||
}
|
||||
|
||||
/* Encodes a MODE 0 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(0, adr, sector);
|
||||
|
||||
sector += 16;
|
||||
|
||||
for (i = 0; i < 2336; i++)
|
||||
*sector++ = 0;
|
||||
}
|
||||
|
||||
/* Encodes a MODE 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(1, adr, sector);
|
||||
|
||||
calc_mode1_edc(sector);
|
||||
|
||||
/* clear the intermediate field */
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 1] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 2] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 3] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 4] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 5] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 6] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 7] = 0;
|
||||
|
||||
calc_P_parity(sector);
|
||||
calc_Q_parity(sector);
|
||||
}
|
||||
|
||||
/* Encodes a MODE 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Encodes a XA form 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
calc_mode2_form1_edc(sector);
|
||||
|
||||
/* P/Q partiy must not contain the sector header so clear it */
|
||||
sector[LEC_HEADER_OFFSET] =
|
||||
sector[LEC_HEADER_OFFSET + 1] =
|
||||
sector[LEC_HEADER_OFFSET + 2] =
|
||||
sector[LEC_HEADER_OFFSET + 3] = 0;
|
||||
|
||||
calc_P_parity(sector);
|
||||
calc_Q_parity(sector);
|
||||
|
||||
/* finally add the sector header */
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Encodes a XA form 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
calc_mode2_form2_edc(sector);
|
||||
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(uint8_t *sector)
|
||||
{
|
||||
uint16_t i;
|
||||
const uint8_t *stable = SCRAMBLE_TABLE;
|
||||
uint8_t *p = sector;
|
||||
uint8_t tmp;
|
||||
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
/* just swap bytes of sector sync */
|
||||
tmp = *p;
|
||||
*p = *(p + 1);
|
||||
p++;
|
||||
*p++ = tmp;
|
||||
}
|
||||
for (;i < (2352 / 2); i++) {
|
||||
/* scramble and swap bytes */
|
||||
tmp = *p ^ *stable++;
|
||||
*p = *(p + 1) ^ *stable++;
|
||||
p++;
|
||||
*p++ = tmp;
|
||||
}
|
||||
}
|
||||
|
72
mednafen/cdrom-0928/lec.h
Normal file
72
mednafen/cdrom-0928/lec.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||
*
|
||||
* Copyright (C) 1998-2002 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LEC_H__
|
||||
#define __LEC_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
/* Encodes a MODE 0 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(uint32_t adr, uint8_t *sector);
|
||||
|
||||
/* Encodes a MODE 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(uint32_t adr, uint8_t *sector);
|
||||
|
||||
/* Encodes a MODE 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(uint32_t adr, uint8_t *sector);
|
||||
|
||||
/* Encodes a XA form 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(uint32_t adr, uint8_t *sector);
|
||||
|
||||
/* Encodes a XA form 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector);
|
||||
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(uint8_t *sector);
|
||||
|
||||
#endif
|
1165
mednafen/cdrom-0928/pcecd.cpp
Normal file
1165
mednafen/cdrom-0928/pcecd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
66
mednafen/cdrom-0928/pcecd.h
Normal file
66
mednafen/cdrom-0928/pcecd.h
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef __PCE_CDROM_H
|
||||
#define __PCE_CDROM_H
|
||||
|
||||
#include <blip/Blip_Buffer.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double CDDA_Volume;
|
||||
|
||||
unsigned int CD_Speed;
|
||||
|
||||
double ADPCM_Volume;
|
||||
|
||||
bool ADPCM_LPF;
|
||||
} PCECD_Settings;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
CD_GSREG_BSY = 0,
|
||||
CD_GSREG_REQ, // RO
|
||||
CD_GSREG_MSG, // RO
|
||||
CD_GSREG_CD, // RO
|
||||
CD_GSREG_IO, // RO
|
||||
CD_GSREG_SEL,
|
||||
|
||||
CD_GSREG_ADPCM_CONTROL,
|
||||
CD_GSREG_ADPCM_FREQ,
|
||||
CD_GSREG_ADPCM_CUR,
|
||||
CD_GSREG_ADPCM_WRADDR,
|
||||
CD_GSREG_ADPCM_RDADDR,
|
||||
CD_GSREG_ADPCM_LENGTH,
|
||||
CD_GSREG_ADPCM_PLAYNIBBLE,
|
||||
|
||||
CD_GSREG_ADPCM_PLAYING,
|
||||
CD_GSREG_ADPCM_HALFREACHED,
|
||||
CD_GSREG_ADPCM_ENDREACHED,
|
||||
};
|
||||
|
||||
uint32 PCECD_GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
||||
void PCECD_SetRegister(const unsigned int id, const uint32 value);
|
||||
|
||||
|
||||
int32 PCECD_Run(uint32 in_timestamp) MDFN_WARN_UNUSED_RESULT;
|
||||
void PCECD_ResetTS(void);
|
||||
|
||||
bool PCECD_Init(const PCECD_Settings *settings, void (*irqcb)(bool), double master_clock, unsigned int ocm, Blip_Buffer *soundbuf_l, Blip_Buffer *soundbuf_r);
|
||||
bool PCECD_SetSettings(const PCECD_Settings *settings);
|
||||
|
||||
void PCECD_Close();
|
||||
|
||||
// Returns number of cycles until next CD event.
|
||||
int32 PCECD_Power(uint32 timestamp) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
uint8 PCECD_Read(uint32 timestamp, uint32, int32 &next_event, const bool PeekMode = false);
|
||||
int32 PCECD_Write(uint32 timestamp, uint32, uint8 data) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
bool PCECD_IsBRAMEnabled();
|
||||
|
||||
int PCECD_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
void ADPCM_PeekRAM(uint32 Address, uint32 Length, uint8 *Buffer);
|
||||
void ADPCM_PokeRAM(uint32 Address, uint32 Length, const uint8 *Buffer);
|
||||
|
||||
#endif
|
||||
|
203
mednafen/cdrom-0928/recover-raw.cpp
Normal file
203
mednafen/cdrom-0928/recover-raw.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
|
||||
static ReedSolomonTables *rt = NULL;
|
||||
|
||||
bool Init_LEC_Correct(void)
|
||||
{
|
||||
gt = CreateGaloisTables(0x11d);
|
||||
rt = CreateReedSolomonTables(gt, 0, 1, 10);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void Kill_LEC_Correct(void)
|
||||
{
|
||||
FreeGaloisTables(gt);
|
||||
FreeReedSolomonTables(rt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** CD level CRC calculation
|
||||
***/
|
||||
|
||||
/*
|
||||
* Test raw sector against its 32bit CRC.
|
||||
* Returns TRUE if frame is good.
|
||||
*/
|
||||
|
||||
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
|
||||
{
|
||||
unsigned int expected_crc, real_crc;
|
||||
unsigned int crc_base = xa_mode ? 2072 : 2064;
|
||||
|
||||
expected_crc = cd_frame[crc_base + 0] << 0;
|
||||
expected_crc |= cd_frame[crc_base + 1] << 8;
|
||||
expected_crc |= cd_frame[crc_base + 2] << 16;
|
||||
expected_crc |= cd_frame[crc_base + 3] << 24;
|
||||
|
||||
if(xa_mode)
|
||||
real_crc = EDCCrc32(cd_frame+16, 2056);
|
||||
else
|
||||
real_crc = EDCCrc32(cd_frame, 2064);
|
||||
|
||||
if(expected_crc == real_crc)
|
||||
return(1);
|
||||
else
|
||||
{
|
||||
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*** A very simple L-EC error correction.
|
||||
***
|
||||
* Perform just one pass over the Q and P vectors to see if everything
|
||||
* is okay respectively correct minor errors. This is pretty much the
|
||||
* same stuff the drive is supposed to do in the final L-EC stage.
|
||||
*/
|
||||
|
||||
static int simple_lec(unsigned char *frame)
|
||||
{
|
||||
unsigned char byte_state[2352];
|
||||
unsigned char p_vector[P_VECTOR_SIZE];
|
||||
unsigned char q_vector[Q_VECTOR_SIZE];
|
||||
unsigned char p_state[P_VECTOR_SIZE];
|
||||
int erasures[Q_VECTOR_SIZE], erasure_count;
|
||||
int ignore[2];
|
||||
int p_failures, q_failures;
|
||||
int p_corrected, q_corrected;
|
||||
int p,q;
|
||||
|
||||
/* Setup */
|
||||
|
||||
memset(byte_state, 0, 2352);
|
||||
|
||||
p_failures = q_failures = 0;
|
||||
p_corrected = q_corrected = 0;
|
||||
|
||||
/* Perform Q-Parity error correction */
|
||||
|
||||
for(q=0; q<N_Q_VECTORS; q++)
|
||||
{ int err;
|
||||
|
||||
/* We have no erasure information for Q vectors */
|
||||
|
||||
GetQVector(frame, q_vector, q);
|
||||
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
|
||||
{ q_failures++;
|
||||
FillQVector(byte_state, 1, q);
|
||||
}
|
||||
else /* Correctable */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetQVector(frame, q_vector, q);
|
||||
q_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform P-Parity error correction */
|
||||
|
||||
for(p=0; p<N_P_VECTORS; p++)
|
||||
{ int err,i;
|
||||
|
||||
/* Try error correction without erasure information */
|
||||
|
||||
GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
|
||||
|
||||
/* If unsuccessful, try again using erasures.
|
||||
Erasure information is uncertain, so try this last. */
|
||||
|
||||
if(err < 0 || err > 2)
|
||||
{ GetPVector(byte_state, p_state, p);
|
||||
erasure_count = 0;
|
||||
|
||||
for(i=0; i<P_VECTOR_SIZE; i++)
|
||||
if(p_state[i])
|
||||
erasures[erasure_count++] = i;
|
||||
|
||||
if(erasure_count > 0 && erasure_count <= 2)
|
||||
{ GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. */
|
||||
{ p_failures++;
|
||||
}
|
||||
else /* Correctable. */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetPVector(frame, p_vector, p);
|
||||
p_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sum up */
|
||||
|
||||
if(q_failures || p_failures || q_corrected || p_corrected)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Validate CD raw sector
|
||||
***/
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode)
|
||||
{
|
||||
int lec_did_sth = FALSE;
|
||||
|
||||
/* Do simple L-EC.
|
||||
It seems that drives stop their internal L-EC as soon as the
|
||||
EDC is okay, so we may see uncorrected errors in the parity bytes.
|
||||
Since we are also interested in the user data only and doing the
|
||||
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
lec_did_sth = simple_lec(frame);
|
||||
}
|
||||
/* Test internal sector checksum again */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
/* EDC failure in RAW sector */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
270
mednafen/cdrom-0928/scsicd-pce-commands.inc
Normal file
270
mednafen/cdrom-0928/scsicd-pce-commands.inc
Normal file
@ -0,0 +1,270 @@
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD8 - SAPSP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPSP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_start;
|
||||
|
||||
//printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default:
|
||||
//SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
|
||||
case 0x00:
|
||||
new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track == toc.last_track + 1)
|
||||
track = 100;
|
||||
else if(track > toc.last_track)
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
|
||||
return;
|
||||
}
|
||||
new_read_sec_start = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
|
||||
{
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
return;
|
||||
}
|
||||
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
read_sec = read_sec_start = new_read_sec_start;
|
||||
read_sec_end = toc.tracks[100].lba;
|
||||
|
||||
|
||||
cdda.CDDAReadPos = 588;
|
||||
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
cdda.PlayMode = PLAYMODE_SILENT;
|
||||
|
||||
if(cdb[1])
|
||||
{
|
||||
cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
}
|
||||
|
||||
if(read_sec < toc.tracks[100].lba)
|
||||
Cur_CDIF->HintReadSector(read_sec);
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD9 - SAPEP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPEP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_end;
|
||||
|
||||
//printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default:
|
||||
//SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
|
||||
case 0x00:
|
||||
new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
|
||||
new_read_sec_end -= 150;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track == toc.last_track + 1)
|
||||
track = 100;
|
||||
else if(track > toc.last_track)
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
|
||||
return;
|
||||
}
|
||||
new_read_sec_end = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
read_sec_end = new_read_sec_end;
|
||||
|
||||
switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask):
|
||||
{
|
||||
default:
|
||||
case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
|
||||
cdda.CDDAStatus = CDDASTATUS_STOPPED;
|
||||
break;
|
||||
}
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDA - Pause *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_PAUSE(const uint8 *cdb)
|
||||
{
|
||||
if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
|
||||
{
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
else // Definitely give an error if it tries to pause when no track is playing!
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDD - Read Subchannel Q *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
|
||||
{
|
||||
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
|
||||
uint8 data_in[8192];
|
||||
|
||||
memset(data_in, 0x00, 10);
|
||||
|
||||
data_in[2] = SubQBuf[1]; // Track
|
||||
data_in[3] = SubQBuf[2]; // Index
|
||||
data_in[4] = SubQBuf[3]; // M(rel)
|
||||
data_in[5] = SubQBuf[4]; // S(rel)
|
||||
data_in[6] = SubQBuf[5]; // F(rel)
|
||||
data_in[7] = SubQBuf[7]; // M(abs)
|
||||
data_in[8] = SubQBuf[8]; // S(abs)
|
||||
data_in[9] = SubQBuf[9]; // F(abs)
|
||||
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
|
||||
data_in[0] = 2; // Pause
|
||||
else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback?
|
||||
data_in[0] = 0; // Playing
|
||||
else
|
||||
data_in[0] = 3; // Stopped
|
||||
|
||||
DoSimpleDataIn(data_in, 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDE - Get Directory Info *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
|
||||
{
|
||||
// Problems:
|
||||
// Returned data lengths on real PCE are not confirmed.
|
||||
// Mode 0x03 behavior not tested on real PCE
|
||||
|
||||
uint8 data_in[2048];
|
||||
uint32 data_in_size = 0;
|
||||
|
||||
memset(data_in, 0, sizeof(data_in));
|
||||
|
||||
switch(cdb[1])
|
||||
{
|
||||
default: MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
printf("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
case 0x0:
|
||||
data_in[0] = U8_to_BCD(toc.first_track);
|
||||
data_in[1] = U8_to_BCD(toc.last_track);
|
||||
|
||||
data_in_size = 2;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
|
||||
LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
|
||||
data_in_size = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(cdb[2] == 0xAA)
|
||||
{
|
||||
track = 100;
|
||||
}
|
||||
else if(track > 99)
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
data_in[3] = toc.tracks[track].control;
|
||||
data_in_size = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DoSimpleDataIn(data_in, data_in_size);
|
||||
}
|
||||
|
3415
mednafen/cdrom-0928/scsicd.cpp
Normal file
3415
mednafen/cdrom-0928/scsicd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
101
mednafen/cdrom-0928/scsicd.h
Normal file
101
mednafen/cdrom-0928/scsicd.h
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef __PCFX_SCSICD_H
|
||||
#define __PCFX_SCSICD_H
|
||||
|
||||
#include <blip/Blip_Buffer.h>
|
||||
|
||||
typedef int32 scsicd_timestamp_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate).
|
||||
uint8 DB;
|
||||
|
||||
uint32 signals;
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
//bool BSY, MSG, CD, REQ, IO;
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
//bool kingACK, kingRST, kingSEL, kingATN;
|
||||
} scsicd_bus_t;
|
||||
|
||||
extern scsicd_bus_t cd_bus; // Don't access this structure directly by name outside of scsicd.c, but use the macros below.
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
#define SCSICD_IO_mask 0x001
|
||||
#define SCSICD_CD_mask 0x002
|
||||
#define SCSICD_MSG_mask 0x004
|
||||
#define SCSICD_REQ_mask 0x008
|
||||
#define SCSICD_BSY_mask 0x010
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
#define SCSICD_kingRST_mask 0x020
|
||||
#define SCSICD_kingACK_mask 0x040
|
||||
#define SCSICD_kingATN_mask 0x080
|
||||
#define SCSICD_kingSEL_mask 0x100
|
||||
|
||||
#define BSY_signal ((const bool)(cd_bus.signals & SCSICD_BSY_mask))
|
||||
#define ACK_signal ((const bool)(cd_bus.signals & SCSICD_kingACK_mask))
|
||||
#define RST_signal ((const bool)(cd_bus.signals & SCSICD_kingRST_mask))
|
||||
#define MSG_signal ((const bool)(cd_bus.signals & SCSICD_MSG_mask))
|
||||
#define SEL_signal ((const bool)(cd_bus.signals & SCSICD_kingSEL_mask))
|
||||
#define REQ_signal ((const bool)(cd_bus.signals & SCSICD_REQ_mask))
|
||||
#define IO_signal ((const bool)(cd_bus.signals & SCSICD_IO_mask))
|
||||
#define CD_signal ((const bool)(cd_bus.signals & SCSICD_CD_mask))
|
||||
#define ATN_signal ((const bool)(cd_bus.signals & SCSICD_kingATN_mask))
|
||||
|
||||
#define DB_signal ((const uint8)cd_bus.DB)
|
||||
|
||||
#define SCSICD_GetDB() DB_signal
|
||||
#define SCSICD_GetBSY() BSY_signal
|
||||
#define SCSICD_GetIO() IO_signal
|
||||
#define SCSICD_GetCD() CD_signal
|
||||
#define SCSICD_GetMSG() MSG_signal
|
||||
#define SCSICD_GetREQ() REQ_signal
|
||||
|
||||
// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)?
|
||||
#define SCSICD_GetACK() ACK_signal
|
||||
#define SCSICD_GetRST() RST_signal
|
||||
#define SCSICD_GetSEL() SEL_signal
|
||||
#define SCSICD_GetATN() ATN_signal
|
||||
|
||||
void SCSICD_Power(scsicd_timestamp_t system_timestamp);
|
||||
void SCSICD_SetDB(uint8 data);
|
||||
|
||||
// These SCSICD_Set* functions are kind of misnomers, at least in comparison to the SCSICD_Get* functions...
|
||||
// They will set/clear the bits corresponding to the KING's side of the bus.
|
||||
void SCSICD_SetACK(bool set);
|
||||
void SCSICD_SetSEL(bool set);
|
||||
void SCSICD_SetRST(bool set);
|
||||
void SCSICD_SetATN(bool set);
|
||||
|
||||
uint32 SCSICD_Run(scsicd_timestamp_t);
|
||||
void SCSICD_ResetTS(void);
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_PCE = 1,
|
||||
SCSICD_PCFX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_IRQ_DATA_TRANSFER_DONE = 1,
|
||||
SCSICD_IRQ_DATA_TRANSFER_READY,
|
||||
SCSICD_IRQ_MAGICAL_REQ,
|
||||
};
|
||||
|
||||
void SCSICD_GetCDDAValues(int16 &left, int16 &right);
|
||||
|
||||
void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...));
|
||||
|
||||
void SCSICD_Init(int type, int CDDATimeDiv, Blip_Buffer *leftbuf, Blip_Buffer *rightbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int));
|
||||
void SCSICD_Close(void);
|
||||
|
||||
void SCSICD_SetTransferRate(uint32 TransferRate);
|
||||
void SCSICD_SetCDDAVolume(double left, double right);
|
||||
int SCSICD_StateAction(StateMem *sm, int load, int data_only, const char *sname);
|
||||
|
||||
void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false);
|
||||
|
||||
#endif
|
@ -15,11 +15,20 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
|
||||
#ifdef HAVE_LIBCDIO
|
||||
#include "CDAccess_Physical.h"
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
@ -33,8 +42,23 @@ CDAccess::~CDAccess()
|
||||
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open(const char *path, bool image_memcache)
|
||||
CDAccess *cdaccess_open_image(const char *path, bool image_memcache)
|
||||
{
|
||||
return new CDAccess_Image(path, image_memcache);
|
||||
CDAccess *ret = NULL;
|
||||
|
||||
if(strlen(path) >= 4 && !strcasecmp(path + strlen(path) - 4, ".ccd"))
|
||||
ret = new CDAccess_CCD(path, image_memcache);
|
||||
else
|
||||
ret = new CDAccess_Image(path, image_memcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open_phys(const char *devicename)
|
||||
{
|
||||
#ifdef HAVE_LIBCDIO
|
||||
return new CDAccess_Physical(devicename);
|
||||
#else
|
||||
throw MDFN_Error(0, _("Physical CD access support not compiled in."));
|
||||
#endif
|
||||
}
|
||||
|
@ -16,9 +16,16 @@ class CDAccess
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
|
||||
|
||||
virtual bool Is_Physical(void) throw() = 0;
|
||||
|
||||
virtual void Eject(bool eject_status) = 0; // Eject a disc if it's physical, otherwise NOP. Returns true on success(or NOP), false on error
|
||||
|
||||
private:
|
||||
CDAccess(const CDAccess&); // No copy constructor.
|
||||
CDAccess& operator=(const CDAccess&); // No assignment operator.
|
||||
};
|
||||
|
||||
CDAccess *cdaccess_open(const char *path, bool image_memcache);
|
||||
CDAccess *cdaccess_open_image(const char *path, bool image_memcache);
|
||||
CDAccess *cdaccess_open_phys(const char *devicename);
|
||||
|
||||
#endif
|
||||
|
427
mednafen/cdrom/CDAccess_CCD.cpp
Normal file
427
mednafen/cdrom/CDAccess_CCD.cpp
Normal file
@ -0,0 +1,427 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "../general.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
#include <trio/trio.h>
|
||||
|
||||
#include <limits>
|
||||
#include <limits.h>
|
||||
#include <map>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
static void MDFN_strtoupper(std::string &str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
|
||||
for(size_t x = 0; x < len; x++)
|
||||
{
|
||||
if(str[x] >= 'a' && str[x] <= 'z')
|
||||
{
|
||||
str[x] = str[x] - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::string> CCD_Section;
|
||||
|
||||
template<typename T>
|
||||
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
|
||||
{
|
||||
CCD_Section::iterator zit = s.find(propname);
|
||||
|
||||
if(zit == s.end())
|
||||
{
|
||||
if(have_defval)
|
||||
return defval;
|
||||
else
|
||||
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
|
||||
}
|
||||
|
||||
const std::string &v = zit->second;
|
||||
int scan_base = 10;
|
||||
size_t scan_offset = 0;
|
||||
long ret = 0;
|
||||
|
||||
if(v.length() >= 3 && v[0] == '0' && v[1] == 'x')
|
||||
{
|
||||
scan_base = 16;
|
||||
scan_offset = 2;
|
||||
}
|
||||
|
||||
const char *vp = v.c_str() + scan_offset;
|
||||
char *ep = NULL;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
ret = strtol(vp, &ep, scan_base);
|
||||
else
|
||||
ret = strtoul(vp, &ep, scan_base);
|
||||
|
||||
if(!vp[0] || ep[0])
|
||||
{
|
||||
throw MDFN_Error(0, _("Property %s: Malformed integer: %s"), propname.c_str(), v.c_str());
|
||||
}
|
||||
|
||||
//if(ret < minv || ret > maxv)
|
||||
//{
|
||||
// throw MDFN_Error(0, _("Property %s: Integer %ld out of range(accepted: %d through %d)."), propname.c_str(), ret, minv, maxv);
|
||||
//}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
CDAccess_CCD::CDAccess_CCD(const char *path, bool image_memcache) : img_stream(NULL), sub_stream(NULL), img_numsectors(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
Load(path, image_memcache);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Load(const char *path, bool image_memcache)
|
||||
{
|
||||
FileStream cf(path, FileStream::MODE_READ);
|
||||
std::map<std::string, CCD_Section> Sections;
|
||||
std::string linebuf;
|
||||
std::string cur_section_name;
|
||||
std::string dir_path, file_base, file_ext;
|
||||
char img_extsd[4] = { 'i', 'm', 'g', 0 };
|
||||
char sub_extsd[4] = { 's', 'u', 'b', 0 };
|
||||
|
||||
MDFN_GetFilePathComponents(path, &dir_path, &file_base, &file_ext);
|
||||
|
||||
if(file_ext.length() == 4 && file_ext[0] == '.')
|
||||
{
|
||||
signed char extupt[3] = { -1, -1, -1 };
|
||||
|
||||
for(int i = 1; i < 4; i++)
|
||||
{
|
||||
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
|
||||
extupt[i - 1] = 'A' - 'a';
|
||||
else if(file_ext[i] >= 'a' && file_ext[i] <= 'z')
|
||||
extupt[i - 1] = 0;
|
||||
}
|
||||
|
||||
signed char av = -1;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] != -1)
|
||||
av = extupt[i];
|
||||
else
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
if(av == -1)
|
||||
av = 0;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] == -1)
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
img_extsd[i] += extupt[i];
|
||||
sub_extsd[i] += extupt[i];
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%s %d %d %d\n", file_ext.c_str(), extupt[0], extupt[1], extupt[2]);
|
||||
|
||||
linebuf.reserve(256);
|
||||
|
||||
while(cf.get_line(linebuf) >= 0)
|
||||
{
|
||||
MDFN_trim(linebuf);
|
||||
|
||||
if(linebuf.length() == 0) // Skip blank lines.
|
||||
continue;
|
||||
|
||||
if(linebuf[0] == '[')
|
||||
{
|
||||
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
|
||||
throw MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
|
||||
|
||||
cur_section_name = linebuf.substr(1, linebuf.length() - 2);
|
||||
MDFN_strtoupper(cur_section_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t feqpos = linebuf.find('=');
|
||||
const size_t leqpos = linebuf.rfind('=');
|
||||
std::string k, v;
|
||||
|
||||
if(feqpos == std::string::npos || feqpos != leqpos)
|
||||
throw MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
|
||||
|
||||
k = linebuf.substr(0, feqpos);
|
||||
v = linebuf.substr(feqpos + 1);
|
||||
|
||||
MDFN_trim(k);
|
||||
MDFN_trim(v);
|
||||
|
||||
MDFN_strtoupper(k);
|
||||
|
||||
Sections[cur_section_name][k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
CCD_Section& ds = Sections["DISC"];
|
||||
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
|
||||
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
|
||||
bool data_tracks_scrambled = CCD_ReadInt<unsigned>(ds, "DATATRACKSSCRAMBLED");
|
||||
|
||||
if(num_sessions != 1)
|
||||
throw MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
|
||||
|
||||
if(data_tracks_scrambled)
|
||||
throw MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
|
||||
|
||||
//printf("MOO: %d\n", toc_entries);
|
||||
|
||||
for(unsigned te = 0; te < toc_entries; te++)
|
||||
{
|
||||
char tmpbuf[64];
|
||||
trio_snprintf(tmpbuf, sizeof(tmpbuf), "ENTRY %u", te);
|
||||
CCD_Section& ts = Sections[std::string(tmpbuf)];
|
||||
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
|
||||
uint8 point = CCD_ReadInt<uint8>(ts, "POINT");
|
||||
uint8 adr = CCD_ReadInt<uint8>(ts, "ADR");
|
||||
uint8 control = CCD_ReadInt<uint8>(ts, "CONTROL");
|
||||
uint8 pmin = CCD_ReadInt<uint8>(ts, "PMIN");
|
||||
uint8 psec = CCD_ReadInt<uint8>(ts, "PSEC");
|
||||
uint8 pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
|
||||
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
|
||||
|
||||
if(session != 1)
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
|
||||
|
||||
// Reference: ECMA-394, page 5-14
|
||||
switch(point)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
|
||||
break;
|
||||
|
||||
case 0xA0:
|
||||
tocd.first_track = pmin;
|
||||
tocd.disc_type = psec;
|
||||
break;
|
||||
|
||||
case 0xA1:
|
||||
tocd.last_track = pmin;
|
||||
break;
|
||||
|
||||
case 0xA2:
|
||||
tocd.tracks[100].adr = adr;
|
||||
tocd.tracks[100].control = control;
|
||||
tocd.tracks[100].lba = plba;
|
||||
break;
|
||||
|
||||
case 1 ... 99:
|
||||
tocd.tracks[point].adr = adr;
|
||||
tocd.tracks[point].control = control;
|
||||
tocd.tracks[point].lba = plba;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience leadout track duplication.
|
||||
if(tocd.last_track < 99)
|
||||
tocd.tracks[tocd.last_track + 1] = tocd.tracks[100];
|
||||
|
||||
//
|
||||
// Open image stream.
|
||||
{
|
||||
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
|
||||
|
||||
if(image_memcache)
|
||||
{
|
||||
img_stream = new MemoryStream(new FileStream(image_path.c_str(), FileStream::MODE_READ));
|
||||
}
|
||||
else
|
||||
{
|
||||
img_stream = new FileStream(image_path.c_str(), FileStream::MODE_READ);
|
||||
}
|
||||
|
||||
int64 ss = img_stream->size();
|
||||
|
||||
if(ss % 2352)
|
||||
throw MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
|
||||
|
||||
img_numsectors = ss / 2352;
|
||||
}
|
||||
|
||||
//
|
||||
// Open subchannel stream
|
||||
{
|
||||
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
|
||||
|
||||
if(image_memcache)
|
||||
sub_stream = new MemoryStream(new FileStream(sub_path.c_str(), FileStream::MODE_READ));
|
||||
else
|
||||
sub_stream = new FileStream(sub_path.c_str(), FileStream::MODE_READ);
|
||||
|
||||
if(sub_stream->size() != (int64)img_numsectors * 96)
|
||||
throw MDFN_Error(0, _("CCD SUB file size mismatch."));
|
||||
}
|
||||
|
||||
CheckSubQSanity();
|
||||
}
|
||||
|
||||
//
|
||||
// Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the
|
||||
// case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we
|
||||
// error out here if a bad rip is detected.
|
||||
//
|
||||
// This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be
|
||||
// used in an effort to "repair" a broken rip.
|
||||
//
|
||||
void CDAccess_CCD::CheckSubQSanity(void)
|
||||
{
|
||||
size_t checksum_pass_counter = 0;
|
||||
int prev_lba = INT_MAX;
|
||||
uint8 prev_track = 0;
|
||||
|
||||
for(size_t s = 0; s < img_numsectors; s++)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8 full[96];
|
||||
struct
|
||||
{
|
||||
uint8 pbuf[12];
|
||||
uint8 qbuf[12];
|
||||
};
|
||||
} buf;
|
||||
|
||||
sub_stream->seek(s * 96, SEEK_SET);
|
||||
sub_stream->read(buf.full, 96);
|
||||
|
||||
if(subq_check_checksum(buf.qbuf))
|
||||
{
|
||||
uint8 adr = buf.qbuf[0] & 0xF;
|
||||
|
||||
if(adr == 0x01)
|
||||
{
|
||||
uint8 track_bcd = buf.qbuf[1];
|
||||
uint8 index_bcd = buf.qbuf[2];
|
||||
uint8 rm_bcd = buf.qbuf[3];
|
||||
uint8 rs_bcd = buf.qbuf[4];
|
||||
uint8 rf_bcd = buf.qbuf[5];
|
||||
uint8 am_bcd = buf.qbuf[7];
|
||||
uint8 as_bcd = buf.qbuf[8];
|
||||
uint8 af_bcd = buf.qbuf[9];
|
||||
|
||||
//printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd);
|
||||
|
||||
if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) ||
|
||||
!BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) ||
|
||||
rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74)
|
||||
{
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
|
||||
uint8 track = BCD_to_U8(track_bcd);
|
||||
|
||||
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(excessively large jump in AMSF)"));
|
||||
|
||||
if(abs(lba - s) > 100)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(AMSF value is out of tolerance)"));
|
||||
|
||||
prev_lba = lba;
|
||||
|
||||
if(track < prev_track)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
|
||||
//else if(prev_track && track - pre
|
||||
|
||||
prev_track = track;
|
||||
}
|
||||
checksum_pass_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Cleanup(void)
|
||||
{
|
||||
if(img_stream)
|
||||
{
|
||||
delete img_stream;
|
||||
img_stream = NULL;
|
||||
}
|
||||
|
||||
if(sub_stream)
|
||||
{
|
||||
delete sub_stream;
|
||||
sub_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CDAccess_CCD::~CDAccess_CCD()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
{
|
||||
if(lba < 0 || (size_t)lba >= img_numsectors)
|
||||
throw(MDFN_Error(0, _("LBA out of range.")));
|
||||
|
||||
uint8 sub_buf[96];
|
||||
|
||||
img_stream->seek(lba * 2352, SEEK_SET);
|
||||
img_stream->read(buf, 2352);
|
||||
|
||||
sub_stream->seek(lba * 96, SEEK_SET);
|
||||
sub_stream->read(sub_buf, 96);
|
||||
|
||||
subpw_interleave(sub_buf, buf + 2352);
|
||||
}
|
||||
|
||||
|
||||
void CDAccess_CCD::Read_TOC(CDUtility::TOC *toc)
|
||||
{
|
||||
*toc = tocd;
|
||||
}
|
||||
|
||||
bool CDAccess_CCD::Is_Physical(void) throw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Eject(bool eject_status)
|
||||
{
|
||||
|
||||
}
|
||||
|
50
mednafen/cdrom/CDAccess_CCD.h
Normal file
50
mednafen/cdrom/CDAccess_CCD.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../FileStream.h"
|
||||
#include "../MemoryStream.h"
|
||||
#include "CDAccess.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CDAccess_CCD : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_CCD(const char *path, bool image_memcache);
|
||||
virtual ~CDAccess_CCD();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void) throw();
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
|
||||
private:
|
||||
|
||||
void Load(const char *path, bool image_memcache);
|
||||
void Cleanup(void);
|
||||
|
||||
void CheckSubQSanity(void);
|
||||
|
||||
Stream* img_stream;
|
||||
Stream* sub_stream;
|
||||
size_t img_numsectors;
|
||||
CDUtility::TOC tocd;
|
||||
};
|
@ -30,10 +30,11 @@
|
||||
Trying to read sectors at an LBA of less than 0 is not supported. TODO: support it(at least up to -150).
|
||||
*/
|
||||
|
||||
#define _CDROMFILE_INTERNAL
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
@ -51,10 +52,6 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../msvc_compat.h"
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
enum
|
||||
@ -112,48 +109,65 @@ static const char *DI_CUE_Strings[7] =
|
||||
"MODE2/2352"
|
||||
};
|
||||
|
||||
static char *UnQuotify(char *src, char *dest)
|
||||
// Should return an offset to the start of the next argument(past any whitespace), or if there isn't a next argument,
|
||||
// it'll return the length of the src string.
|
||||
static size_t UnQuotify(const std::string &src, size_t source_offset, std::string &dest, bool parse_quotes = true)
|
||||
{
|
||||
const size_t source_len = src.length();
|
||||
bool in_quote = 0;
|
||||
bool already_normal = 0;
|
||||
|
||||
while(*src)
|
||||
dest.clear();
|
||||
|
||||
while(source_offset < source_len)
|
||||
{
|
||||
if(*src == ' ' || *src == '\t')
|
||||
if(src[source_offset] == ' ' || src[source_offset] == '\t')
|
||||
{
|
||||
if(!in_quote)
|
||||
{
|
||||
if(already_normal)
|
||||
if(already_normal) // Trailing whitespace(IE we're done with this argument)
|
||||
break;
|
||||
else
|
||||
else // Leading whitespace, ignore it.
|
||||
{
|
||||
src++;
|
||||
source_offset++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*src == '"')
|
||||
if(src[source_offset] == '"' && parse_quotes)
|
||||
{
|
||||
if(in_quote)
|
||||
{
|
||||
src++;
|
||||
source_offset++;
|
||||
// Not sure which behavior is most useful(or correct :b).
|
||||
#if 0
|
||||
in_quote = false;
|
||||
already_normal = true;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
in_quote = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest = *src;
|
||||
dest.push_back(src[source_offset]);
|
||||
already_normal = 1;
|
||||
dest++;
|
||||
}
|
||||
src++;
|
||||
source_offset++;
|
||||
}
|
||||
|
||||
*dest = 0;
|
||||
return(src);
|
||||
while(source_offset < source_len)
|
||||
{
|
||||
if(src[source_offset] != ' ' && src[source_offset] != '\t')
|
||||
break;
|
||||
|
||||
source_offset++;
|
||||
}
|
||||
|
||||
return source_offset;
|
||||
}
|
||||
|
||||
uint32 CDAccess_Image::GetSectorCount(CDRFILE_TRACK_INFO *track)
|
||||
@ -183,39 +197,45 @@ uint32 CDAccess_Image::GetSectorCount(CDRFILE_TRACK_INFO *track)
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool CDAccess_Image::ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const char *filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache)
|
||||
void CDAccess_Image::ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache)
|
||||
{
|
||||
long offset = 0; // In bytes!
|
||||
long tmp_long;
|
||||
int m, s, f;
|
||||
uint32 sector_mult;
|
||||
long sectors;
|
||||
std::string efn;
|
||||
std::map<std::string, Stream*>::iterator ribbit;
|
||||
|
||||
efn = MDFN_EvalFIP(base_dir, filename);
|
||||
ribbit = toc_streamcache.find(filename);
|
||||
|
||||
FILE *dummy = fopen(efn.c_str(), "rb");
|
||||
if(ribbit != toc_streamcache.end())
|
||||
{
|
||||
track->FirstFileInstance = 0;
|
||||
|
||||
// test if file exists - if not exit prematurely
|
||||
|
||||
if(dummy)
|
||||
fclose(dummy);
|
||||
track->fp = ribbit->second;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
{
|
||||
std::string efn;
|
||||
|
||||
track->fp = new FileStream(efn.c_str(), FileStream::MODE_READ);
|
||||
if(MDFN_GetSettingB("libretro.cd_load_into_ram"))
|
||||
track->fp = new MemoryStream(track->fp);
|
||||
track->FirstFileInstance = 1;
|
||||
|
||||
if(strlen(filename) >= 4 && !strcasecmp(filename + strlen(filename) - 4, ".wav"))
|
||||
efn = MDFN_EvalFIP(base_dir, filename);
|
||||
|
||||
if(image_memcache)
|
||||
track->fp = new MemoryStream(new FileStream(efn.c_str(), FileStream::MODE_READ));
|
||||
else
|
||||
track->fp = new FileStream(efn.c_str(), FileStream::MODE_READ);
|
||||
|
||||
toc_streamcache[filename] = track->fp;
|
||||
}
|
||||
|
||||
if(filename.length() >= 4 && !strcasecmp(filename.c_str() + filename.length() - 4, ".wav"))
|
||||
{
|
||||
track->AReader = AR_Open(track->fp);
|
||||
|
||||
if(!track->AReader)
|
||||
{
|
||||
fprintf(stderr, "TODO ERROR.\n");
|
||||
return false;
|
||||
}
|
||||
throw MDFN_Error(0, "TODO ERROR");
|
||||
}
|
||||
|
||||
sector_mult = DI_Size_Table[track->DIFormat];
|
||||
@ -261,29 +281,71 @@ bool CDAccess_Image::ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int t
|
||||
|
||||
if(tmp_long > sectors)
|
||||
{
|
||||
fprintf(stderr, "Length specified in TOC file for track %d is too large by %ld sectors!\n", tracknum, (long)(tmp_long - sectors));
|
||||
return false;
|
||||
throw MDFN_Error(0, _("Length specified in TOC file for track %d is too large by %ld sectors!\n"), tracknum, (long)(tmp_long - sectors));
|
||||
}
|
||||
sectors = tmp_long;
|
||||
}
|
||||
|
||||
track->FirstFileInstance = 1;
|
||||
track->sectors = sectors;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void MDFN_strtoupper(char *str)
|
||||
{
|
||||
for(size_t x = 0; str[x]; x++)
|
||||
{
|
||||
if(str[x] >= 'a' && str[x] <= 'z')
|
||||
{
|
||||
str[x] = str[x] - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MDFN_strtoupper(std::string &str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
|
||||
for(size_t x = 0; x < len; x++)
|
||||
{
|
||||
if(str[x] >= 'a' && str[x] <= 'z')
|
||||
{
|
||||
str[x] = str[x] - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::string MDFN_toupper(const std::string &str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
std::string new_str;
|
||||
|
||||
new_str.reserve(len);
|
||||
|
||||
for(size_t x = 0; x < len; x++)
|
||||
{
|
||||
int c = str[x];
|
||||
|
||||
if(c >= 'a' && c <= 'z')
|
||||
c = c - 'a' + 'A';
|
||||
|
||||
new_str.push_back(c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
{
|
||||
FileWrapper fp(path, FileWrapper::MODE_READ);
|
||||
MemoryStream fp(new FileStream(path, FileStream::MODE_READ));
|
||||
static const unsigned max_args = 4;
|
||||
std::string linebuf;
|
||||
std::string cmdbuf, args[max_args];
|
||||
bool IsTOC = FALSE;
|
||||
char linebuf[512];
|
||||
int32 active_track = -1;
|
||||
int32 AutoTrackInc = 1; // For TOC
|
||||
CDRFILE_TRACK_INFO TmpTrack;
|
||||
std::string file_base, file_ext;
|
||||
std::map<std::string, Stream*> toc_streamcache;
|
||||
|
||||
memset(&TmpTrack, 0, sizeof(TmpTrack));
|
||||
|
||||
@ -291,7 +353,7 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
|
||||
if(!strcasecmp(file_ext.c_str(), ".toc"))
|
||||
{
|
||||
puts("TOC file detected.");
|
||||
MDFN_printf(_("TOC file detected.\n"));
|
||||
IsTOC = true;
|
||||
}
|
||||
|
||||
@ -314,53 +376,46 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
FirstTrack = 99;
|
||||
LastTrack = 0;
|
||||
|
||||
while(fp.get_line(linebuf, 512) != NULL)
|
||||
linebuf.reserve(1024);
|
||||
while(fp.get_line(linebuf) >= 0)
|
||||
{
|
||||
char cmdbuf[512], raw_args[512], args[4][512];
|
||||
int argcount = 0;
|
||||
|
||||
raw_args[0] = 0;
|
||||
cmdbuf[0] = 0;
|
||||
|
||||
args[0][0] = args[1][0] = args[2][0] = args[3][0] = 0;
|
||||
|
||||
MDFN_trim(linebuf);
|
||||
unsigned argcount = 0;
|
||||
|
||||
if(IsTOC)
|
||||
{
|
||||
// Handle TOC format comments
|
||||
char *ss_loc = strstr(linebuf, "//");
|
||||
if(ss_loc)
|
||||
{
|
||||
ss_loc[0] = '\n';
|
||||
ss_loc[1] = 0;
|
||||
}
|
||||
size_t ss_loc = linebuf.find("//");
|
||||
|
||||
if(ss_loc != std::string::npos)
|
||||
linebuf.resize(ss_loc);
|
||||
}
|
||||
|
||||
if(trio_sscanf(linebuf, "%s %[^\r\n]", cmdbuf, raw_args) < 1)
|
||||
continue; // Skip blank lines
|
||||
|
||||
UnQuotify(UnQuotify(UnQuotify(UnQuotify(raw_args, args[0]), args[1]), args[2]), args[3]);
|
||||
if(args[0][0])
|
||||
// Call trim AFTER we handle TOC-style comments, so we'll be sure to remove trailing whitespace in lines like: MONKEY // BABIES
|
||||
MDFN_trim(linebuf);
|
||||
|
||||
if(linebuf.length() == 0) // Skip blank lines.
|
||||
continue;
|
||||
|
||||
// Grab command and arguments.
|
||||
{
|
||||
argcount++;
|
||||
if(args[1][0])
|
||||
{
|
||||
argcount++;
|
||||
if(args[2][0])
|
||||
{
|
||||
argcount++;
|
||||
if(args[3][0])
|
||||
{
|
||||
argcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t offs = 0;
|
||||
|
||||
offs = UnQuotify(linebuf, offs, cmdbuf, false);
|
||||
for(argcount = 0; argcount < max_args && offs < linebuf.length(); argcount++)
|
||||
offs = UnQuotify(linebuf, offs, args[argcount]);
|
||||
|
||||
// Make sure unused arguments are cleared out so we don't have inter-line leaks!
|
||||
for(unsigned x = argcount; x < max_args; x++)
|
||||
args[x].clear();
|
||||
|
||||
MDFN_strtoupper(cmdbuf);
|
||||
}
|
||||
|
||||
//printf("%s\n", cmdbuf.c_str()); //: %s %s %s %s\n", cmdbuf.c_str(), args[0].c_str(), args[1].c_str(), args[2].c_str(), args[3].c_str());
|
||||
|
||||
if(IsTOC)
|
||||
{
|
||||
if(!strcasecmp(cmdbuf, "TRACK"))
|
||||
if(cmdbuf == "TRACK")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
@ -383,7 +438,7 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
int format_lookup;
|
||||
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
|
||||
{
|
||||
if(!strcasecmp(args[0], DI_CDRDAO_Strings[format_lookup]))
|
||||
if(!strcasecmp(args[0].c_str(), DI_CDRDAO_Strings[format_lookup]))
|
||||
{
|
||||
TmpTrack.DIFormat = format_lookup;
|
||||
break;
|
||||
@ -392,92 +447,92 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
|
||||
if(format_lookup == _DI_FORMAT_COUNT)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s"), args[0]));
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s"), args[0].c_str()));
|
||||
}
|
||||
|
||||
if(TmpTrack.DIFormat == DI_FORMAT_AUDIO)
|
||||
TmpTrack.RawAudioMSBFirst = TRUE; // Silly cdrdao...
|
||||
|
||||
if(!strcasecmp(args[1], "RW"))
|
||||
if(!strcasecmp(args[1].c_str(), "RW"))
|
||||
{
|
||||
TmpTrack.SubchannelMode = CDRF_SUBM_RW;
|
||||
throw(MDFN_Error(0, _("\"RW\" format subchannel data not supported, only \"RW_RAW\" is!")));
|
||||
}
|
||||
else if(!strcasecmp(args[1], "RW_RAW"))
|
||||
else if(!strcasecmp(args[1].c_str(), "RW_RAW"))
|
||||
TmpTrack.SubchannelMode = CDRF_SUBM_RW_RAW;
|
||||
|
||||
} // end to TRACK
|
||||
else if(!strcasecmp(cmdbuf, "SILENCE"))
|
||||
else if(cmdbuf == "SILENCE")
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "ZERO"))
|
||||
else if(cmdbuf == "ZERO")
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "FILE") || !strcasecmp(cmdbuf, "AUDIOFILE"))
|
||||
else if(cmdbuf == "FILE" || cmdbuf == "AUDIOFILE")
|
||||
{
|
||||
const char *binoffset = NULL;
|
||||
const char *msfoffset = NULL;
|
||||
const char *length = NULL;
|
||||
|
||||
if(args[1][0] == '#')
|
||||
if(args[1].c_str()[0] == '#')
|
||||
{
|
||||
binoffset = args[1] + 1;
|
||||
msfoffset = args[2];
|
||||
length = args[3];
|
||||
binoffset = args[1].c_str() + 1;
|
||||
msfoffset = args[2].c_str();
|
||||
length = args[3].c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
msfoffset = args[1];
|
||||
length = args[2];
|
||||
msfoffset = args[1].c_str();
|
||||
length = args[2].c_str();
|
||||
}
|
||||
//printf("%s, %s, %s, %s\n", args[0], binoffset, msfoffset, length);
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
//printf("%s, %s, %s, %s\n", args[0].c_str(), binoffset, msfoffset, length);
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length, image_memcache, toc_streamcache);
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "DATAFILE"))
|
||||
else if(cmdbuf == "DATAFILE")
|
||||
{
|
||||
const char *binoffset = NULL;
|
||||
const char *length = NULL;
|
||||
|
||||
if(args[1][0] == '#')
|
||||
if(args[1].c_str()[0] == '#')
|
||||
{
|
||||
binoffset = args[1] + 1;
|
||||
length = args[2];
|
||||
binoffset = args[1].c_str() + 1;
|
||||
length = args[2].c_str();
|
||||
}
|
||||
else
|
||||
length = args[1];
|
||||
length = args[1].c_str();
|
||||
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length, image_memcache, toc_streamcache);
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "INDEX"))
|
||||
else if(cmdbuf == "INDEX")
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "PREGAP"))
|
||||
else if(cmdbuf == "PREGAP")
|
||||
{
|
||||
if(active_track < 0)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf));
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf.c_str()));
|
||||
}
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
trio_sscanf(args[0].c_str(), "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
} // end to PREGAP
|
||||
else if(!strcasecmp(cmdbuf, "START"))
|
||||
else if(cmdbuf == "START")
|
||||
{
|
||||
if(active_track < 0)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf));
|
||||
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf.c_str()));
|
||||
}
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
trio_sscanf(args[0].c_str(), "%d:%d:%d", &m, &s, &f);
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
} /*********** END TOC HANDLING ************/
|
||||
else // now for CUE sheet handling
|
||||
{
|
||||
if(!strcasecmp(cmdbuf, "FILE"))
|
||||
if(cmdbuf == "FILE")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
@ -488,40 +543,38 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
|
||||
if(!MDFN_IsFIROPSafe(args[0]))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting.\n"), args[0]));
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting.\n"), args[0].c_str()));
|
||||
}
|
||||
|
||||
std::string efn = MDFN_EvalFIP(base_dir, args[0]);
|
||||
TmpTrack.fp = new FileStream(efn.c_str(), FileStream::MODE_READ);
|
||||
TmpTrack.FirstFileInstance = 1;
|
||||
|
||||
if(MDFN_GetSettingB("libretro.cd_load_into_ram"))
|
||||
if(image_memcache)
|
||||
TmpTrack.fp = new MemoryStream(TmpTrack.fp);
|
||||
|
||||
if(!strcasecmp(args[1], "BINARY"))
|
||||
if(!strcasecmp(args[1].c_str(), "BINARY"))
|
||||
{
|
||||
//TmpTrack.Format = TRACK_FORMAT_DATA;
|
||||
//struct stat stat_buf;
|
||||
//fstat(fileno(TmpTrack.fp), &stat_buf);
|
||||
//TmpTrack.sectors = stat_buf.st_size; // / 2048;
|
||||
}
|
||||
#ifdef NEED_TREMOR
|
||||
else if(!strcasecmp(args[1], "OGG") || !strcasecmp(args[1], "VORBIS") || !strcasecmp(args[1], "WAVE") || !strcasecmp(args[1], "WAV") || !strcasecmp(args[1], "PCM")
|
||||
|| !strcasecmp(args[1], "MPC") || !strcasecmp(args[1], "MP+"))
|
||||
else if(!strcasecmp(args[1].c_str(), "OGG") || !strcasecmp(args[1].c_str(), "VORBIS") || !strcasecmp(args[1].c_str(), "WAVE") || !strcasecmp(args[1].c_str(), "WAV") || !strcasecmp(args[1].c_str(), "PCM")
|
||||
|| !strcasecmp(args[1].c_str(), "MPC") || !strcasecmp(args[1].c_str(), "MP+"))
|
||||
{
|
||||
TmpTrack.AReader = AR_Open(TmpTrack.fp);
|
||||
if(!TmpTrack.AReader)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Unsupported audio track file format: %s\n"), args[0]));
|
||||
throw(MDFN_Error(0, _("Unsupported audio track file format: %s\n"), args[0].c_str()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw(MDFN_Error(0, _("Unsupported track format: %s\n"), args[1]));
|
||||
throw(MDFN_Error(0, _("Unsupported track format: %s\n"), args[1].c_str()));
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "TRACK"))
|
||||
else if(cmdbuf == "TRACK")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
@ -533,7 +586,7 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
TmpTrack.index[0] = -1;
|
||||
TmpTrack.index[1] = 0;
|
||||
}
|
||||
active_track = atoi(args[0]);
|
||||
active_track = atoi(args[0].c_str());
|
||||
|
||||
if(active_track < FirstTrack)
|
||||
FirstTrack = active_track;
|
||||
@ -543,7 +596,7 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
int format_lookup;
|
||||
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
|
||||
{
|
||||
if(!strcasecmp(args[1], DI_CUE_Strings[format_lookup]))
|
||||
if(!strcasecmp(args[1].c_str(), DI_CUE_Strings[format_lookup]))
|
||||
{
|
||||
TmpTrack.DIFormat = format_lookup;
|
||||
break;
|
||||
@ -552,7 +605,7 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
|
||||
if(format_lookup == _DI_FORMAT_COUNT)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s\n"), args[0]));
|
||||
throw(MDFN_Error(0, _("Invalid track format: %s\n"), args[1].c_str()));
|
||||
}
|
||||
|
||||
if(active_track < 0 || active_track > 99)
|
||||
@ -560,49 +613,63 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
throw(MDFN_Error(0, _("Invalid track number: %d\n"), active_track));
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "INDEX"))
|
||||
else if(cmdbuf == "INDEX")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[1], "%d:%d:%d", &m, &s, &f);
|
||||
unsigned int m,s,f;
|
||||
|
||||
if(!strcasecmp(args[0], "01") || !strcasecmp(args[0], "1"))
|
||||
if(trio_sscanf(args[1].c_str(), "%u:%u:%u", &m, &s, &f) != 3)
|
||||
{
|
||||
throw MDFN_Error(0, _("Malformed m:s:f time in \"%s\" directive: %s"), cmdbuf.c_str(), args[0].c_str());
|
||||
}
|
||||
|
||||
if(!strcasecmp(args[0].c_str(), "01") || !strcasecmp(args[0].c_str(), "1"))
|
||||
TmpTrack.index[1] = (m * 60 + s) * 75 + f;
|
||||
else if(!strcasecmp(args[0], "00") || !strcasecmp(args[0], "0"))
|
||||
else if(!strcasecmp(args[0].c_str(), "00") || !strcasecmp(args[0].c_str(), "0"))
|
||||
TmpTrack.index[0] = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "PREGAP"))
|
||||
else if(cmdbuf == "PREGAP")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
unsigned int m,s,f;
|
||||
|
||||
if(trio_sscanf(args[0].c_str(), "%u:%u:%u", &m, &s, &f) != 3)
|
||||
{
|
||||
throw MDFN_Error(0, _("Malformed m:s:f time in \"%s\" directive: %s"), cmdbuf.c_str(), args[0].c_str());
|
||||
}
|
||||
|
||||
TmpTrack.pregap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "POSTGAP"))
|
||||
else if(cmdbuf == "POSTGAP")
|
||||
{
|
||||
if(active_track >= 0)
|
||||
{
|
||||
int m,s,f;
|
||||
trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f);
|
||||
unsigned int m,s,f;
|
||||
|
||||
if(trio_sscanf(args[0].c_str(), "%u:%u:%u", &m, &s, &f) != 3)
|
||||
{
|
||||
throw MDFN_Error(0, _("Malformed m:s:f time in \"%s\" directive: %s"), cmdbuf.c_str(), args[0].c_str());
|
||||
}
|
||||
|
||||
TmpTrack.postgap = (m * 60 + s) * 75 + f;
|
||||
}
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "REM"))
|
||||
else if(cmdbuf == "REM")
|
||||
{
|
||||
|
||||
}
|
||||
else if(!strcasecmp(cmdbuf, "CDTEXTFILE") || !strcasecmp(cmdbuf, "FLAGS") || !strcasecmp(cmdbuf, "CATALOG") || !strcasecmp(cmdbuf, "ISRC") ||
|
||||
!strcasecmp(cmdbuf, "TITLE") || !strcasecmp(cmdbuf, "PERFORMER") || !strcasecmp(cmdbuf, "SONGWRITER"))
|
||||
else if(cmdbuf == "CDTEXTFILE" || cmdbuf == "FLAGS" || cmdbuf == "CATALOG" || cmdbuf == "ISRC" ||
|
||||
cmdbuf == "TITLE" || cmdbuf == "PERFORMER" || cmdbuf == "SONGWRITER")
|
||||
{
|
||||
MDFN_printf(_("Unsupported CUE sheet directive: \"%s\".\n"), cmdbuf); // FIXME, generic logger passed by pointer to constructor
|
||||
MDFN_printf(_("Unsupported CUE sheet directive: \"%s\".\n"), cmdbuf.c_str()); // FIXME, generic logger passed by pointer to constructor
|
||||
}
|
||||
else
|
||||
{
|
||||
throw MDFN_Error(0, _("Unknown CUE sheet directive \"%s\".\n"), cmdbuf);
|
||||
throw MDFN_Error(0, _("Unknown CUE sheet directive \"%s\".\n"), cmdbuf.c_str());
|
||||
}
|
||||
} // end of CUE sheet handling
|
||||
} // end of fgets() loop
|
||||
@ -620,7 +687,6 @@ void CDAccess_Image::ImageOpen(const char *path, bool image_memcache)
|
||||
|
||||
int32 RunningLBA = 0;
|
||||
int32 LastIndex = 0;
|
||||
(void)LastIndex;
|
||||
long FileOffset = 0;
|
||||
|
||||
for(int x = FirstTrack; x < (FirstTrack + NumTracks); x++)
|
||||
@ -713,8 +779,17 @@ void CDAccess_Image::Cleanup(void)
|
||||
|
||||
CDAccess_Image::CDAccess_Image(const char *path, bool image_memcache) : NumTracks(0), FirstTrack(0), LastTrack(0), total_sectors(0)
|
||||
{
|
||||
memset(Tracks, 0, sizeof(Tracks));
|
||||
ImageOpen(path, MDFN_GetSettingB("libretro.cd_load_into_ram"));
|
||||
memset(Tracks, 0, sizeof(Tracks));
|
||||
|
||||
try
|
||||
{
|
||||
ImageOpen(path, image_memcache);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CDAccess_Image::~CDAccess_Image()
|
||||
@ -829,8 +904,37 @@ void CDAccess_Image::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
|
||||
if(!TrackFound)
|
||||
{
|
||||
fprintf(stderr, "Could not find track for sector %u!\n", lba);
|
||||
throw(MDFN_Error(0, _("Could not find track for sector %u!"), lba));
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(qbuf[0] & 0x40)
|
||||
{
|
||||
uint8 dummy_buf[2352 + 96];
|
||||
bool any_mismatch = FALSE;
|
||||
|
||||
memcpy(dummy_buf + 16, buf + 16, 2048);
|
||||
memset(dummy_buf + 2352, 0, 96);
|
||||
|
||||
MakeSubPQ(lba, dummy_buf + 2352);
|
||||
encode_mode1_sector(lba + 150, dummy_buf);
|
||||
|
||||
for(int i = 0; i < 2352 + 96; i++)
|
||||
{
|
||||
if(dummy_buf[i] != buf[i])
|
||||
{
|
||||
printf("Mismatch at %d, %d: %02x:%02x; ", lba, i, dummy_buf[i], buf[i]);
|
||||
any_mismatch = TRUE;
|
||||
}
|
||||
}
|
||||
if(any_mismatch)
|
||||
puts("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
//subq_deinterleave(buf + 2352, qbuf);
|
||||
//printf("%02x\n", qbuf[0]);
|
||||
//printf("%02x\n", buf[12 + 3]);
|
||||
}
|
||||
|
||||
void CDAccess_Image::MakeSubPQ(int32 lba, uint8 *SubPWBuf)
|
||||
@ -952,7 +1056,7 @@ void CDAccess_Image::Read_TOC(TOC *toc)
|
||||
toc->tracks[toc->last_track + 1] = toc->tracks[100];
|
||||
}
|
||||
|
||||
bool CDAccess_Image::Is_Physical(void)
|
||||
bool CDAccess_Image::Is_Physical(void) throw()
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef __MDFN_CDACCESS_IMAGE_H
|
||||
#define __MDFN_CDACCESS_IMAGE_H
|
||||
|
||||
#include <map>
|
||||
|
||||
class Stream;
|
||||
class AudioReader;
|
||||
|
||||
@ -29,6 +31,33 @@ struct CDRFILE_TRACK_INFO
|
||||
|
||||
AudioReader *AReader;
|
||||
};
|
||||
#if 0
|
||||
struct Medium_Chunk
|
||||
{
|
||||
int64 Offset; // Offset in [..TODO..]
|
||||
uint32 DIFormat;
|
||||
|
||||
FILE *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
AudioReader *AReader;
|
||||
};
|
||||
|
||||
struct CD_Chunk
|
||||
{
|
||||
int32 LBA;
|
||||
int32 Track;
|
||||
int32 Index;
|
||||
bool DataType;
|
||||
|
||||
Medium_Chunk Medium;
|
||||
};
|
||||
|
||||
static std::vector<CD_Chunk> Chunks;
|
||||
#endif
|
||||
|
||||
class CDAccess_Image : public CDAccess
|
||||
{
|
||||
@ -41,7 +70,7 @@ class CDAccess_Image : public CDAccess
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void);
|
||||
virtual bool Is_Physical(void) throw();
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
private:
|
||||
@ -60,7 +89,7 @@ class CDAccess_Image : public CDAccess
|
||||
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
|
||||
void MakeSubPQ(int32 lba, uint8 *SubPWBuf);
|
||||
|
||||
bool ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const char *filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache);
|
||||
void ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache);
|
||||
uint32 GetSectorCount(CDRFILE_TRACK_INFO *track);
|
||||
};
|
||||
|
||||
|
364
mednafen/cdrom/CDAccess_Physical.cpp
Normal file
364
mednafen/cdrom/CDAccess_Physical.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define EXTERNAL_LIBCDIO_CONFIG_H 1
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "../general.h"
|
||||
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Physical.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <cdio/cdio.h>
|
||||
#include <cdio/mmc.h>
|
||||
//#include <cdio/logging.h>
|
||||
|
||||
#if LIBCDIO_VERSION_NUM >= 83
|
||||
#include <cdio/mmc_cmds.h>
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
void CDAccess_Physical::DetermineFeatures(void)
|
||||
{
|
||||
uint8 buf[256];
|
||||
|
||||
mmc_cdb_t cdb = {{0, }};
|
||||
|
||||
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
cdb.field[2] = 0x2A;
|
||||
|
||||
cdb.field[7] = sizeof(buf) >> 8;
|
||||
cdb.field[8] = sizeof(buf) & 0xFF;
|
||||
|
||||
if(mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
|
||||
&cdb,
|
||||
SCSI_MMC_DATA_READ,
|
||||
sizeof(buf),
|
||||
buf))
|
||||
{
|
||||
throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command failed.")));
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8 *pd = &buf[8];
|
||||
|
||||
if(pd[0] != 0x2A || pd[1] < 0x14)
|
||||
{
|
||||
throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command returned bogus data for mode page 0x2A.")));
|
||||
}
|
||||
|
||||
if(!(pd[4] & 0x10))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 1 sectors.")));
|
||||
}
|
||||
|
||||
if(!(pd[4] & 0x20))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 2 sectors.")));
|
||||
}
|
||||
|
||||
if(!(pd[5] & 0x01))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Reading CD-DA sectors via \"READ CD\" is not supported.")));
|
||||
}
|
||||
|
||||
if(!(pd[5] & 0x02))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Read CD-DA sectors via \"READ CD\" are not positionally-accurate.")));
|
||||
}
|
||||
|
||||
if(!(pd[5] & 0x04))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Reading raw subchannel data via \"READ CD\" is not supported.")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDAccess_Physical::PreventAllowMediumRemoval(bool prevent)
|
||||
{
|
||||
#if 0
|
||||
mmc_cdb_t cdb = {{0, }};
|
||||
uint8 buf[8];
|
||||
|
||||
cdb.field[0] = 0x1E;
|
||||
cdb.field[1] = 0x00;
|
||||
cdb.field[2] = 0x00;
|
||||
cdb.field[3] = 0x00;
|
||||
cdb.field[4] = 0x00; //prevent;
|
||||
cdb.field[5] = 0x00;
|
||||
|
||||
printf("%d\n", mmc_run_cmd_len (p_cdio, MMC_TIMEOUT_DEFAULT,
|
||||
&cdb, 6,
|
||||
SCSI_MMC_DATA_READ, 0, buf));
|
||||
assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CDAccess_Physical::ReadPhysDiscInfo(unsigned retry)
|
||||
{
|
||||
mmc_cdb_t cdb = {{0, }};
|
||||
uint8 toc_buffer[8192];
|
||||
int64 start_time = time(NULL);
|
||||
int cdio_rc;
|
||||
|
||||
cdb.field[0] = 0x43; // Read TOC
|
||||
cdb.field[1] = 0x00;
|
||||
cdb.field[2] = 0x00; // Format 0000b
|
||||
cdb.field[3] = 0x00;
|
||||
cdb.field[4] = 0x00;
|
||||
cdb.field[5] = 0x00;
|
||||
cdb.field[6] = 0x01; // Track number
|
||||
cdb.field[7] = sizeof(toc_buffer) >> 8;
|
||||
cdb.field[8] = sizeof(toc_buffer) & 0xFF;
|
||||
cdb.field[9] = 0x00;
|
||||
|
||||
memset(toc_buffer, 0, sizeof(toc_buffer));
|
||||
|
||||
while((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
|
||||
&cdb,
|
||||
SCSI_MMC_DATA_READ,
|
||||
sizeof(toc_buffer),
|
||||
toc_buffer)))
|
||||
{
|
||||
if(!retry || time(NULL) >= (start_time + retry))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Error reading disc TOC.")));
|
||||
}
|
||||
}
|
||||
|
||||
PhysTOC.Clear();
|
||||
|
||||
PhysTOC.first_track = toc_buffer[2];
|
||||
PhysTOC.last_track = toc_buffer[3];
|
||||
|
||||
if(PhysTOC.first_track < 1 || PhysTOC.first_track > 99)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid first track: %d\n"), PhysTOC.first_track));
|
||||
}
|
||||
|
||||
if(PhysTOC.last_track > 99 || PhysTOC.last_track < PhysTOC.first_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Invalid last track: %d\n"), PhysTOC.last_track));
|
||||
}
|
||||
|
||||
int32 len_counter = MDFN_de16msb(&toc_buffer[0]) - 2;
|
||||
uint8 *tbi = &toc_buffer[4];
|
||||
|
||||
assert(len_counter >= 0);
|
||||
assert((len_counter & 7) == 0);
|
||||
|
||||
while(len_counter)
|
||||
{
|
||||
uint8 adr = tbi[1] >> 4;
|
||||
uint8 control = tbi[1] & 0xF;
|
||||
uint8 tnum = tbi[2];
|
||||
uint32 lba = MDFN_de32msb(&tbi[4]);
|
||||
|
||||
if(tnum == 0xAA)
|
||||
{
|
||||
PhysTOC.tracks[100].adr = adr;
|
||||
PhysTOC.tracks[100].control = control;
|
||||
PhysTOC.tracks[100].lba = lba;
|
||||
}
|
||||
else if(tnum >= PhysTOC.first_track && tnum <= PhysTOC.last_track)
|
||||
{
|
||||
PhysTOC.tracks[tnum].adr = adr;
|
||||
PhysTOC.tracks[tnum].control = control;
|
||||
PhysTOC.tracks[tnum].lba = lba;
|
||||
}
|
||||
|
||||
tbi += 8;
|
||||
len_counter -= 8;
|
||||
}
|
||||
|
||||
// Convenience leadout track duplication.
|
||||
if(PhysTOC.last_track < 99)
|
||||
PhysTOC.tracks[PhysTOC.last_track + 1] = PhysTOC.tracks[100];
|
||||
}
|
||||
|
||||
void CDAccess_Physical::Read_TOC(TOC *toc)
|
||||
{
|
||||
*toc = PhysTOC;
|
||||
}
|
||||
|
||||
void CDAccess_Physical::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
{
|
||||
mmc_cdb_t cdb = {{0, }};
|
||||
int cdio_rc;
|
||||
|
||||
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);
|
||||
CDIO_MMC_SET_READ_TYPE (cdb.field, CDIO_MMC_READ_TYPE_ANY);
|
||||
CDIO_MMC_SET_READ_LBA (cdb.field, lba);
|
||||
CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1);
|
||||
|
||||
if(SkipSectorRead[(lba >> 3) & 0xFFFF] & (1 << (lba & 7)))
|
||||
{
|
||||
printf("Read(skipped): %d\n", lba);
|
||||
memset(buf, 0, 2352);
|
||||
|
||||
cdb.field[9] = 0x00;
|
||||
cdb.field[10] = 0x01;
|
||||
|
||||
if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
|
||||
&cdb,
|
||||
SCSI_MMC_DATA_READ,
|
||||
96,
|
||||
buf + 2352)))
|
||||
{
|
||||
throw(MDFN_Error(0, _("MMC Read Error; libcdio return code %d"), cdio_rc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cdb.field[9] = 0xF8;
|
||||
cdb.field[10] = 0x01;
|
||||
|
||||
if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
|
||||
&cdb,
|
||||
SCSI_MMC_DATA_READ,
|
||||
2352 + 96,
|
||||
buf)))
|
||||
{
|
||||
throw(MDFN_Error(0, _("MMC Read Error; libcdio return code %d"), cdio_rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static void nlh(cdio_log_level_t level, const char message[])
|
||||
//{
|
||||
// printf("%s\n", message);
|
||||
//}
|
||||
|
||||
CDAccess_Physical::CDAccess_Physical(const char *path)
|
||||
{
|
||||
char **devices = NULL;
|
||||
char **parseit = NULL;
|
||||
|
||||
p_cdio = NULL;
|
||||
|
||||
cdio_init();
|
||||
//cdio_log_set_handler(nlh);
|
||||
//
|
||||
//
|
||||
//
|
||||
try
|
||||
{
|
||||
devices = cdio_get_devices(DRIVER_DEVICE);
|
||||
parseit = devices;
|
||||
if(parseit)
|
||||
{
|
||||
MDFN_printf(_("Connected physical devices:\n"));
|
||||
MDFN_indent(1);
|
||||
while(*parseit)
|
||||
{
|
||||
MDFN_printf("%s\n", *parseit);
|
||||
parseit++;
|
||||
}
|
||||
MDFN_indent(-1);
|
||||
}
|
||||
|
||||
if(!parseit || parseit == devices)
|
||||
{
|
||||
throw(MDFN_Error(0, _("No CDROM drives detected(or no disc present).")));
|
||||
}
|
||||
|
||||
if(devices)
|
||||
{
|
||||
cdio_free_device_list(devices);
|
||||
devices = NULL;
|
||||
}
|
||||
|
||||
p_cdio = cdio_open_cd(path);
|
||||
if(!p_cdio)
|
||||
{
|
||||
throw(MDFN_Error(0, _("Unknown error opening physical CD")));
|
||||
}
|
||||
|
||||
//PreventAllowMediumRemoval(true);
|
||||
ReadPhysDiscInfo(0);
|
||||
|
||||
//
|
||||
// Determine how we can read this CD.
|
||||
//
|
||||
DetermineFeatures();
|
||||
|
||||
memset(SkipSectorRead, 0, sizeof(SkipSectorRead));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
if(devices)
|
||||
cdio_free_device_list(devices);
|
||||
|
||||
if(p_cdio)
|
||||
cdio_destroy((CdIo *)p_cdio);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CDAccess_Physical::~CDAccess_Physical()
|
||||
{
|
||||
cdio_destroy((CdIo *)p_cdio);
|
||||
}
|
||||
|
||||
bool CDAccess_Physical::Is_Physical(void) throw()
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
void CDAccess_Physical::Eject(bool eject_status)
|
||||
{
|
||||
int cdio_rc;
|
||||
|
||||
#if LIBCDIO_VERSION_NUM >= 83
|
||||
if((cdio_rc = mmc_start_stop_unit((CdIo *)p_cdio, eject_status, false, 0, 0)) != 0)
|
||||
{
|
||||
if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.
|
||||
throw(MDFN_Error(0, _("Error ejecting medium;; libcdio return code %d"), cdio_rc));
|
||||
}
|
||||
#else
|
||||
if((cdio_rc = mmc_start_stop_media((CdIo *)p_cdio, eject_status, false, 0)) != 0)
|
||||
{
|
||||
if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.
|
||||
throw(MDFN_Error(0, _("Error ejecting medium;; libcdio return code %d"), cdio_rc));
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!eject_status)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReadPhysDiscInfo(10);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
#if LIBCDIO_VERSION_NUM >= 83
|
||||
mmc_start_stop_unit((CdIo *)p_cdio, true, false, 0, 0); // Eject disc, if possible.
|
||||
#else
|
||||
mmc_start_stop_media((CdIo *)p_cdio, true, false, 0); // Eject disc, if possible.
|
||||
#endif
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
36
mednafen/cdrom/CDAccess_Physical.h
Normal file
36
mednafen/cdrom/CDAccess_Physical.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef __MDFN_CDACCESS_PHYSICAL_H
|
||||
#define __MDFN_CDACCESS_PHYSICAL_H
|
||||
|
||||
// Don't include <cdio.h> here, else it will pollute with its #define's.
|
||||
|
||||
class CDAccess_Physical : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_Physical(const char *path);
|
||||
virtual ~CDAccess_Physical();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void) throw();
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
private:
|
||||
|
||||
void *p_cdio;
|
||||
|
||||
void DetermineFeatures(void);
|
||||
void PhysOpen(const char *path);
|
||||
void ReadPhysDiscInfo(unsigned retry);
|
||||
|
||||
void PreventAllowMediumRemoval(bool prevent);
|
||||
|
||||
CDUtility::TOC PhysTOC;
|
||||
|
||||
// TODO: 1-bit per sector on the physical CD. If set, don't read that sector.
|
||||
uint8 SkipSectorRead[65536];
|
||||
};
|
||||
|
||||
#endif
|
@ -17,11 +17,15 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
#include "../mednafen.h"
|
||||
#include "CDUtility.h"
|
||||
#include "dvdisaster.h"
|
||||
#include "lec.h"
|
||||
|
||||
#include <assert.h>
|
||||
// Kill_LEC_Correct();
|
||||
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
|
||||
@ -60,14 +64,41 @@ static uint16 subq_crctab[256] =
|
||||
};
|
||||
|
||||
|
||||
static uint8 scramble_table[2352 - 12];
|
||||
|
||||
static bool CDUtility_Inited = false;
|
||||
|
||||
static void InitScrambleTable(void)
|
||||
{
|
||||
unsigned cv = 1;
|
||||
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
{
|
||||
unsigned char z = 0;
|
||||
|
||||
for(int b = 0; b < 8; b++)
|
||||
{
|
||||
z |= (cv & 1) << b;
|
||||
|
||||
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
|
||||
cv = (cv >> 1) | (feedback << 14);
|
||||
}
|
||||
|
||||
scramble_table[i - 12] = z;
|
||||
}
|
||||
|
||||
//for(int i = 0; i < 2352 - 12; i++)
|
||||
// printf("0x%02x, ", scramble_table[i]);
|
||||
}
|
||||
|
||||
void CDUtility_Init(void)
|
||||
{
|
||||
if(!CDUtility_Inited)
|
||||
{
|
||||
Init_LEC_Correct();
|
||||
|
||||
InitScrambleTable();
|
||||
|
||||
CDUtility_Inited = true;
|
||||
}
|
||||
}
|
||||
@ -114,7 +145,7 @@ bool edc_check(const uint8 *sector_data, bool xa)
|
||||
return(CheckEDC(sector_data, xa));
|
||||
}
|
||||
|
||||
bool edc_lec_check_correct(uint8 *sector_data, bool xa)
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
@ -159,4 +190,59 @@ void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
|
||||
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
memset(out_buf, 0, 96);
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for(unsigned i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
for(unsigned d = 0; d < 12; d++)
|
||||
{
|
||||
for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
uint8 rawb = 0;
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output)
|
||||
{
|
||||
assert(subq_check_checksum(subq_input));
|
||||
|
||||
|
||||
subq_generate_checksum(subq_output);
|
||||
}
|
||||
#endif
|
||||
|
||||
void scrambleize_data_sector(uint8 *sector_data)
|
||||
{
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
sector_data[i] ^= scramble_table[i - 12];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ namespace CDUtility
|
||||
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||
// Returns "false" if errors couldn't be corrected.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_lec_check_correct(uint8 *sector_data, bool xa);
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
|
||||
|
||||
//
|
||||
// Subchannel(Q in particular) functions
|
||||
@ -200,11 +200,20 @@ namespace CDUtility
|
||||
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
|
||||
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
|
||||
// Only valid for ADR_CURPOS.
|
||||
// subq_input must pass subq_check_checksum().
|
||||
// TODO
|
||||
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
|
||||
|
||||
// (De)Scrambles data sector.
|
||||
void scrambleize_data_sector(uint8 *sector_data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
mednafen_SOURCES += cdrom/audioreader.cpp cdrom/cdromif.cpp cdrom/scsicd.cpp cdrom/pcecd.cpp
|
||||
mednafen_SOURCES += cdrom/CDUtility.cpp cdrom/crc32.cpp cdrom/galois.cpp cdrom/l-ec.cpp cdrom/recover-raw.cpp
|
||||
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp
|
||||
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp cdrom/CDAccess_CCD.cpp
|
||||
|
||||
if HAVE_LIBCDIO
|
||||
mednafen_SOURCES += cdrom/CDAccess_Physical.cpp
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __MDFN_SIMPLEFIFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../math_ops.h"
|
||||
|
||||
@ -40,6 +41,8 @@ class SimpleFIFO
|
||||
{
|
||||
T ret;
|
||||
|
||||
assert(in_count > 0);
|
||||
|
||||
ret = data[read_pos];
|
||||
|
||||
if(!peek)
|
||||
@ -53,11 +56,15 @@ class SimpleFIFO
|
||||
|
||||
INLINE uint8 ReadByte(bool peek = false)
|
||||
{
|
||||
assert(sizeof(T) == 1);
|
||||
|
||||
return(ReadUnit(peek));
|
||||
}
|
||||
|
||||
INLINE void Write(const T *happy_data, uint32 happy_count)
|
||||
{
|
||||
assert(CanWrite() >= happy_count);
|
||||
|
||||
while(happy_count)
|
||||
{
|
||||
data[write_pos] = *happy_data;
|
||||
@ -76,6 +83,7 @@ class SimpleFIFO
|
||||
|
||||
INLINE void WriteByte(const T& wr_data)
|
||||
{
|
||||
assert(sizeof(T) == 1);
|
||||
Write(&wr_data, 1);
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,22 @@
|
||||
// Don't allow exceptions to propagate into the vorbis/musepack/etc. libraries, as it could easily leave the state of the library's decoder "object" in an
|
||||
// inconsistent state, which would cause all sorts of unfun when we try to destroy it while handling the exception farther up.
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen.h"
|
||||
#include "audioreader.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../tremor/ivorbisfile.h"
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
#include <sndfile.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPUSFILE
|
||||
#include "audioreader_opus.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
@ -74,8 +84,6 @@ int64 AudioReader::FrameCount(void)
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef NEED_TREMOR
|
||||
|
||||
class OggVorbisReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
@ -99,30 +107,58 @@ static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_dat
|
||||
if(!size)
|
||||
return(0);
|
||||
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
try
|
||||
{
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
fw->close();
|
||||
return(0);
|
||||
try
|
||||
{
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static long iov_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
return fw->tell();
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
|
||||
@ -175,7 +211,6 @@ int64 OggVorbisReader::FrameCount(void)
|
||||
{
|
||||
return(ov_pcm_total(&ovfile, -1));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
**
|
||||
@ -189,9 +224,150 @@ int64 OggVorbisReader::FrameCount(void)
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
class SFReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
|
||||
SFReader(Stream *fp);
|
||||
~SFReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
SNDFILE *sf;
|
||||
SF_INFO sfinfo;
|
||||
SF_VIRTUAL_IO sfvf;
|
||||
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
static sf_count_t isf_get_filelen(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->size();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
//printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
|
||||
|
||||
fw->seek(offset, whence);
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" SEEK FAILED\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
sf_count_t ret = fw->read(ptr, count, false);
|
||||
|
||||
//printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" READ FAILED\n");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
static sf_count_t isf_tell(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
SFReader::SFReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
memset(&sfvf, 0, sizeof(sfvf));
|
||||
sfvf.get_filelen = isf_get_filelen;
|
||||
sfvf.seek = isf_seek;
|
||||
sfvf.read = isf_read;
|
||||
sfvf.write = isf_write;
|
||||
sfvf.tell = isf_tell;
|
||||
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
|
||||
throw(0);
|
||||
}
|
||||
|
||||
SFReader::~SFReader()
|
||||
{
|
||||
sf_close(sf);
|
||||
}
|
||||
|
||||
int64 SFReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
|
||||
}
|
||||
|
||||
bool SFReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
// FIXME error condition
|
||||
if(sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
|
||||
return(false);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 SFReader::FrameCount(void)
|
||||
{
|
||||
return(sfinfo.frames);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
AudioReader *AR_Open(Stream *fp)
|
||||
{
|
||||
#ifdef NEED_TREMOR
|
||||
#ifdef HAVE_OPUSFILE
|
||||
try
|
||||
{
|
||||
return new OpusReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
return new OggVorbisReader(fp);
|
||||
@ -199,6 +375,15 @@ AudioReader *AR_Open(Stream *fp)
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
try
|
||||
{
|
||||
return new SFReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
return(NULL);
|
||||
|
@ -14,9 +14,11 @@ class AudioReader
|
||||
{
|
||||
int64 ret;
|
||||
|
||||
//if(frame_offset >= 0)
|
||||
{
|
||||
if(LastReadPos != frame_offset)
|
||||
{
|
||||
//puts("SEEK");
|
||||
if(!Seek_(frame_offset))
|
||||
return(0);
|
||||
LastReadPos = frame_offset;
|
||||
|
185
mednafen/cdrom/audioreader_opus.cpp
Normal file
185
mednafen/cdrom/audioreader_opus.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "audioreader.h"
|
||||
#include "audioreader_opus.h"
|
||||
|
||||
// OPUS SUPPORT NOT DONE YET!!!
|
||||
/*
|
||||
|
||||
(int64)op_pcm_total() * 44100 / 48000
|
||||
|
||||
resampling vs seek, filter delay, etc. to consider
|
||||
*/
|
||||
|
||||
static size_t iop_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
if(!size)
|
||||
return(0);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int iop_seek_func(void *user_data, opus_int64 offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int iop_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static opus_int64 iop_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Error strings copied from libopusfile header file comments. */
|
||||
static const char *op_errstring(int error)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
int code;
|
||||
const char *str;
|
||||
} error_table[] =
|
||||
{
|
||||
{ OP_EREAD, gettext_noop("OP_EREAD: An underlying read, seek, or tell operation failed when it should have succeeded.") },
|
||||
{ OP_EFAULT, gettext_noop("OP_EFAULT: A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.") },
|
||||
{ OP_EIMPL, gettext_noop("OP_EIMPL: The stream used a feature that is not implemented, such as an unsupported channel family.") },
|
||||
{ OP_EINVAL, gettext_noop("OP_EINVAL: One or more parameters to a function were invalid.") },
|
||||
{ OP_ENOTFORMAT, gettext_noop("OP_ENOTFORMAT: A purported Ogg Opus stream did not begin with an Ogg page, or a purported header packet did not start with one of the required strings, \"OpusHead\" or \"OpusTags\".") },
|
||||
{ OP_EBADHEADER, gettext_noop("OP_EBADHEADER: A required header packet was not properly formatted, contained illegal values, or was missing altogether.") },
|
||||
{ OP_EVERSION, gettext_noop("OP_EVERSION: The ID header contained an unrecognized version number.") },
|
||||
{ OP_EBADPACKET, gettext_noop("OP_EBADPACKET: An audio packet failed to decode properly.") },
|
||||
{ OP_EBADLINK, gettext_noop("OP_EBADLINK: We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.") },
|
||||
{ OP_ENOSEEK, gettext_noop("OP_ENOSEEK: An operation that requires seeking was requested on an unseekable stream.") },
|
||||
{ OP_EBADTIMESTAMP, gettext_noop("OP_EBADTIMESTAMP: The first or last granule position of a link failed basic validity checks.") },
|
||||
};
|
||||
|
||||
for(unsigned i = 0; i < sizeof(error_table) / sizeof(error_table[0]); i++)
|
||||
{
|
||||
if(error_table[i].code == error)
|
||||
{
|
||||
return _(error_table[i].str);
|
||||
}
|
||||
}
|
||||
|
||||
return _("Unknown");
|
||||
}
|
||||
|
||||
OggOpusReader::OggOpusReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
OpusFileCallbacks cb;
|
||||
int error = 0;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.read_func = iop_read_func;
|
||||
cb.seek_func = iop_seek_func;
|
||||
cb.close_func = iop_close_func;
|
||||
cb.tell_func = iop_tell_func;
|
||||
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
if(!(opfile = op_open_callbacks((void*)fp, &cb, NULL, 0, &error)))
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(0, _("opusfile: error code: %d(%s)", error, op_errstring(error)));
|
||||
break;
|
||||
|
||||
case OP_ENOTFORMAT:
|
||||
throw(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OggOpusReader::~OggOpusReader()
|
||||
{
|
||||
op_free(opfile);
|
||||
}
|
||||
|
||||
int64 OggOpusReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
int16 *tr_buffer = buffer;
|
||||
int64 tr_count = frames * 2;
|
||||
|
||||
while(tr_count > 0)
|
||||
{
|
||||
int64 didread = op_read(opfile, tr_buffer, tr_count, NULL);
|
||||
|
||||
if(didread == 0)
|
||||
break;
|
||||
|
||||
tr_buffer += didread * 2;
|
||||
tr_count -= didread * 2;
|
||||
}
|
||||
|
||||
return(frames - (tr_count / 2));
|
||||
}
|
||||
|
||||
bool OggOpusReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
op_pcm_seek(opfile, frame_offset);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 OggOpusReader::FrameCount(void)
|
||||
{
|
||||
return(op_pcm_total(pvfile, -1));
|
||||
}
|
21
mednafen/cdrom/audioreader_opus.h
Normal file
21
mednafen/cdrom/audioreader_opus.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef __MDFN_AUDIOREADER_OPUS_H
|
||||
#define __MDFN_AUDIOREADER_OPUS_H
|
||||
|
||||
#include <opus/opusfile.h>
|
||||
|
||||
class OggOpusReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
OggOpusReader(Stream *fp);
|
||||
~OggOpusReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
OggOpus_File *opfile;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
#endif
|
@ -22,7 +22,6 @@
|
||||
#include "cdromif.h"
|
||||
#include "CDAccess.h"
|
||||
#include "../general.h"
|
||||
#include "../mednafen-driver.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -47,6 +46,112 @@ enum
|
||||
CDIF_MSG_EJECT, // Emu -> read, args[0]; 0=insert, 1=eject
|
||||
};
|
||||
|
||||
class CDIF_Message
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Message();
|
||||
CDIF_Message(unsigned int message_, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0, uint32 arg3 = 0);
|
||||
CDIF_Message(unsigned int message_, const std::string &str);
|
||||
~CDIF_Message();
|
||||
|
||||
unsigned int message;
|
||||
uint32 args[4];
|
||||
void *parg;
|
||||
std::string str_message;
|
||||
};
|
||||
|
||||
class CDIF_Queue
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Queue();
|
||||
~CDIF_Queue();
|
||||
|
||||
bool Read(CDIF_Message *message, bool blocking = TRUE);
|
||||
|
||||
void Write(const CDIF_Message &message);
|
||||
|
||||
private:
|
||||
std::queue<CDIF_Message> ze_queue;
|
||||
MDFN_Mutex *ze_mutex;
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool valid;
|
||||
bool error;
|
||||
uint32 lba;
|
||||
uint8 data[2352 + 96];
|
||||
} CDIF_Sector_Buffer;
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_MT : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_MT(CDAccess *cda);
|
||||
virtual ~CDIF_MT();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
// FIXME: Semi-private:
|
||||
int ReadThreadStart(void);
|
||||
|
||||
private:
|
||||
|
||||
CDAccess *disc_cdaccess;
|
||||
|
||||
MDFN_Thread *CDReadThread;
|
||||
|
||||
// Queue for messages to the read thread.
|
||||
CDIF_Queue ReadThreadQueue;
|
||||
|
||||
// Queue for messages to the emu thread.
|
||||
CDIF_Queue EmuThreadQueue;
|
||||
|
||||
|
||||
enum { SBSize = 256 };
|
||||
CDIF_Sector_Buffer SectorBuffers[SBSize];
|
||||
|
||||
uint32 SBWritePos;
|
||||
|
||||
MDFN_Mutex *SBMutex;
|
||||
|
||||
|
||||
//
|
||||
// Read-thread-only:
|
||||
//
|
||||
void RT_EjectDisc(bool eject_status, bool skip_actual_eject = false);
|
||||
|
||||
uint32 ra_lba;
|
||||
int ra_count;
|
||||
uint32 last_read_lba;
|
||||
};
|
||||
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_ST : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_ST(CDAccess *cda);
|
||||
virtual ~CDIF_ST();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
private:
|
||||
CDAccess *disc_cdaccess;
|
||||
};
|
||||
|
||||
CDIF::CDIF() : UnrecoverableError(false), is_phys_cache(false), DiscEjected(false)
|
||||
{
|
||||
|
||||
@ -136,6 +241,7 @@ void CDIF_Queue::Write(const CDIF_Message &message)
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
}
|
||||
|
||||
|
||||
void CDIF_MT::RT_EjectDisc(bool eject_status, bool skip_actual_eject)
|
||||
{
|
||||
int32 old_de = DiscEjected;
|
||||
@ -187,9 +293,17 @@ int CDIF_MT::ReadThreadStart()
|
||||
ra_count = 0;
|
||||
last_read_lba = ~0U;
|
||||
|
||||
RT_EjectDisc(false, true);
|
||||
try
|
||||
{
|
||||
RT_EjectDisc(false, true);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_FATAL_ERROR, std::string(e.what())));
|
||||
return(0);
|
||||
}
|
||||
|
||||
is_phys_cache = false /* will never be physical CD access */;
|
||||
is_phys_cache = disc_cdaccess->Is_Physical();
|
||||
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
|
||||
@ -208,8 +322,15 @@ int CDIF_MT::ReadThreadStart()
|
||||
break;
|
||||
|
||||
case CDIF_MSG_EJECT:
|
||||
RT_EjectDisc(msg.args[0]);
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
try
|
||||
{
|
||||
RT_EjectDisc(msg.args[0]);
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_FATAL_ERROR, std::string(e.what())));
|
||||
}
|
||||
break;
|
||||
|
||||
case CDIF_MSG_READ_SECTOR:
|
||||
@ -219,6 +340,8 @@ int CDIF_MT::ReadThreadStart()
|
||||
static const int speedmult_ra = 2;
|
||||
uint32 new_lba = msg.args[0];
|
||||
|
||||
assert((unsigned int)max_ra < (SBSize / 4));
|
||||
|
||||
if(last_read_lba != ~0U && new_lba == (last_read_lba + 1))
|
||||
{
|
||||
int how_far_ahead = ra_lba - new_lba;
|
||||
@ -252,7 +375,16 @@ int CDIF_MT::ReadThreadStart()
|
||||
uint8 tmpbuf[2352 + 96];
|
||||
bool error_condition = false;
|
||||
|
||||
disc_cdaccess->Read_Raw_Sector(tmpbuf, ra_lba);
|
||||
try
|
||||
{
|
||||
disc_cdaccess->Read_Raw_Sector(tmpbuf, ra_lba);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Sector %u read error: %s"), ra_lba, e.what());
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
error_condition = true;
|
||||
}
|
||||
|
||||
MDFND_LockMutex(SBMutex);
|
||||
|
||||
@ -314,7 +446,7 @@ CDIF_MT::CDIF_MT(CDAccess *cda) : disc_cdaccess(cda), CDReadThread(NULL), SBMute
|
||||
|
||||
CDIF_MT::~CDIF_MT()
|
||||
{
|
||||
bool thread_murdered_with_kitchen_knife = false;
|
||||
bool thread_deaded_failed = false;
|
||||
|
||||
try
|
||||
{
|
||||
@ -323,11 +455,10 @@ CDIF_MT::~CDIF_MT()
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFND_PrintError(e.what());
|
||||
MDFND_KillThread(CDReadThread);
|
||||
thread_murdered_with_kitchen_knife = true;
|
||||
thread_deaded_failed = true;
|
||||
}
|
||||
|
||||
if(!thread_murdered_with_kitchen_knife)
|
||||
if(!thread_deaded_failed)
|
||||
MDFND_WaitThread(CDReadThread, NULL);
|
||||
|
||||
if(SBMutex)
|
||||
@ -350,7 +481,7 @@ bool CDIF::ValidateRawSector(uint8 *buf)
|
||||
if(mode != 0x1 && mode != 0x2)
|
||||
return(false);
|
||||
|
||||
if(!edc_lec_check_correct(buf, mode == 2))
|
||||
if(!edc_lec_check_and_correct(buf, mode == 2))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
@ -479,6 +610,100 @@ bool CDIF_MT::Eject(bool eject_status)
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Single-threaded implementation follows.
|
||||
//
|
||||
//
|
||||
|
||||
CDIF_ST::CDIF_ST(CDAccess *cda) : disc_cdaccess(cda)
|
||||
{
|
||||
//puts("***WARNING USING SINGLE-THREADED CD READER***");
|
||||
|
||||
is_phys_cache = disc_cdaccess->Is_Physical();
|
||||
UnrecoverableError = false;
|
||||
DiscEjected = false;
|
||||
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
|
||||
CDIF_ST::~CDIF_ST()
|
||||
{
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CDIF_ST::HintReadSector(uint32 lba)
|
||||
{
|
||||
// TODO: disc_cdaccess seek hint? (probably not, would require asynchronousitycamel)
|
||||
}
|
||||
|
||||
bool CDIF_ST::ReadRawSector(uint8 *buf, uint32 lba)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
{
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
disc_cdaccess->Read_Raw_Sector(buf, lba);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Sector %u read error: %s"), lba, e.what());
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool CDIF_ST::Eject(bool eject_status)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
try
|
||||
{
|
||||
int32 old_de = DiscEjected;
|
||||
|
||||
DiscEjected = eject_status;
|
||||
|
||||
if(old_de != DiscEjected)
|
||||
{
|
||||
disc_cdaccess->Eject(eject_status);
|
||||
|
||||
if(!eject_status) // Re-read the TOC
|
||||
{
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError("%s", e.what());
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
class CDIF_Stream_Thing : public Stream
|
||||
{
|
||||
public:
|
||||
@ -533,7 +758,14 @@ void CDIF_Stream_Thing::unmap(void)
|
||||
uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
|
||||
{
|
||||
if(count > (((uint64)sector_count * 2048) - position))
|
||||
{
|
||||
if(error_on_eos)
|
||||
{
|
||||
throw MDFN_Error(0, "EOF");
|
||||
}
|
||||
|
||||
count = ((uint64)sector_count * 2048) - position;
|
||||
}
|
||||
|
||||
if(!count)
|
||||
return(0);
|
||||
@ -542,7 +774,12 @@ uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
|
||||
{
|
||||
uint8 buf[2048];
|
||||
|
||||
cdintf->ReadSector(buf, start_lba + (rp / 2048), 1);
|
||||
if(!cdintf->ReadSector(buf, start_lba + (rp / 2048), 1))
|
||||
{
|
||||
throw MDFN_Error(ErrnoHolder(EIO));
|
||||
}
|
||||
|
||||
//::printf("Meow: %08llx -- %08llx\n", count, (rp - position) + std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
|
||||
memcpy((uint8*)data + (rp - position), buf + (rp & 2047), std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
|
||||
}
|
||||
|
||||
@ -558,20 +795,31 @@ void CDIF_Stream_Thing::write(const void *data, uint64 count)
|
||||
|
||||
void CDIF_Stream_Thing::seek(int64 offset, int whence)
|
||||
{
|
||||
int64 new_position;
|
||||
|
||||
switch(whence)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(ErrnoHolder(EINVAL));
|
||||
break;
|
||||
|
||||
case SEEK_SET:
|
||||
position = offset;
|
||||
new_position = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
position += offset;
|
||||
new_position = position + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
position = ((int64)sector_count * 2048) + offset;
|
||||
new_position = ((int64)sector_count * 2048) + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if(new_position < 0 || new_position > ((int64)sector_count * 2048))
|
||||
throw MDFN_Error(ErrnoHolder(EINVAL));
|
||||
|
||||
position = new_position;
|
||||
}
|
||||
|
||||
int64 CDIF_Stream_Thing::tell(void)
|
||||
@ -596,9 +844,17 @@ Stream *CDIF::MakeStream(uint32 lba, uint32 sector_count)
|
||||
}
|
||||
|
||||
|
||||
CDIF *CDIF_Open(const char *path, bool image_memcache)
|
||||
CDIF *CDIF_Open(const char *path, const bool is_device, bool image_memcache)
|
||||
{
|
||||
CDAccess *cda = cdaccess_open(path, image_memcache);
|
||||
if(is_device)
|
||||
return new CDIF_MT(cdaccess_open_phys(path));
|
||||
else
|
||||
{
|
||||
CDAccess *cda = cdaccess_open_image(path, image_memcache);
|
||||
|
||||
return new CDIF_MT(cda);
|
||||
if(!image_memcache)
|
||||
return new CDIF_MT(cda);
|
||||
else
|
||||
return new CDIF_ST(cda);
|
||||
}
|
||||
}
|
||||
|
@ -20,20 +20,11 @@
|
||||
|
||||
#include "CDUtility.h"
|
||||
#include "../Stream.h"
|
||||
#include "CDAccess.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
typedef CDUtility::TOC CD_TOC;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool valid;
|
||||
bool error;
|
||||
uint32 lba;
|
||||
uint8 data[2352 + 96];
|
||||
} CDIF_Sector_Buffer;
|
||||
|
||||
class CDIF
|
||||
{
|
||||
public:
|
||||
@ -74,86 +65,6 @@ class CDIF
|
||||
bool DiscEjected;
|
||||
};
|
||||
|
||||
class CDIF_Message
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Message();
|
||||
CDIF_Message(unsigned int message_, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0, uint32 arg3 = 0);
|
||||
CDIF_Message(unsigned int message_, const std::string &str);
|
||||
~CDIF_Message();
|
||||
|
||||
unsigned int message;
|
||||
uint32 args[4];
|
||||
void *parg;
|
||||
std::string str_message;
|
||||
};
|
||||
|
||||
class CDIF_Queue
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Queue();
|
||||
~CDIF_Queue();
|
||||
|
||||
bool Read(CDIF_Message *message, bool blocking = TRUE);
|
||||
|
||||
void Write(const CDIF_Message &message);
|
||||
|
||||
private:
|
||||
std::queue<CDIF_Message> ze_queue;
|
||||
MDFN_Mutex *ze_mutex;
|
||||
};
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_MT : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_MT(CDAccess *cda);
|
||||
virtual ~CDIF_MT();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
// FIXME: Semi-private:
|
||||
int ReadThreadStart(void);
|
||||
|
||||
private:
|
||||
|
||||
CDAccess *disc_cdaccess;
|
||||
|
||||
MDFN_Thread *CDReadThread;
|
||||
|
||||
// Queue for messages to the read thread.
|
||||
CDIF_Queue ReadThreadQueue;
|
||||
|
||||
// Queue for messages to the emu thread.
|
||||
CDIF_Queue EmuThreadQueue;
|
||||
|
||||
|
||||
enum { SBSize = 256 };
|
||||
CDIF_Sector_Buffer SectorBuffers[SBSize];
|
||||
|
||||
uint32 SBWritePos;
|
||||
|
||||
MDFN_Mutex *SBMutex;
|
||||
|
||||
|
||||
//
|
||||
// Read-thread-only:
|
||||
//
|
||||
void RT_EjectDisc(bool eject_status, bool skip_actual_eject = false);
|
||||
|
||||
uint32 ra_lba;
|
||||
int ra_count;
|
||||
uint32 last_read_lba;
|
||||
};
|
||||
|
||||
CDIF *CDIF_Open(const char *path, bool image_memcache);
|
||||
CDIF *CDIF_Open(const char *path, const bool is_device, bool image_memcache);
|
||||
|
||||
#endif
|
||||
|
130
mednafen/cdrom/crc32.cpp
Normal file
130
mednafen/cdrom/crc32.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* CRC32 code based upon public domain code by Ross Williams (see notes below)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
/***
|
||||
*** EDC checksum used in CDROM sectors
|
||||
***/
|
||||
|
||||
/*****************************************************************/
|
||||
/* */
|
||||
/* CRC LOOKUP TABLE */
|
||||
/* ================ */
|
||||
/* The following CRC lookup table was generated automagically */
|
||||
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||
/* Program V1.0 using the following model parameters: */
|
||||
/* */
|
||||
/* Width : 4 bytes. */
|
||||
/* Poly : 0x8001801BL */
|
||||
/* Reverse : TRUE. */
|
||||
/* */
|
||||
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||
/* see the document titled "A Painless Guide to CRC Error */
|
||||
/* Detection Algorithms" by Ross Williams */
|
||||
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
unsigned long edctable[256] =
|
||||
{
|
||||
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
|
||||
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
|
||||
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
|
||||
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
|
||||
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
|
||||
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
|
||||
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
|
||||
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
|
||||
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
|
||||
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
|
||||
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
|
||||
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
|
||||
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
|
||||
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
|
||||
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
|
||||
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
|
||||
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
|
||||
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
|
||||
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
|
||||
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
|
||||
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
|
||||
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
|
||||
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
|
||||
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
|
||||
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
|
||||
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
|
||||
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
|
||||
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
|
||||
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
|
||||
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
|
||||
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
|
||||
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
|
||||
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
|
||||
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
|
||||
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
|
||||
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
|
||||
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
|
||||
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
|
||||
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
|
||||
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
|
||||
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
|
||||
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
|
||||
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
|
||||
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
|
||||
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
|
||||
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
|
||||
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
|
||||
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
|
||||
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
|
||||
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
|
||||
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
|
||||
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
|
||||
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
|
||||
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
|
||||
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
|
||||
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
|
||||
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
|
||||
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
|
||||
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
|
||||
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
|
||||
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
|
||||
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
|
||||
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
|
||||
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
|
||||
};
|
||||
|
||||
/*
|
||||
* CDROM EDC calculation
|
||||
*/
|
||||
|
||||
uint32 EDCCrc32(const unsigned char *data, int len)
|
||||
{
|
||||
uint32 crc = 0;
|
||||
|
||||
while(len--)
|
||||
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
|
||||
|
||||
return crc;
|
||||
}
|
@ -35,18 +35,21 @@
|
||||
*** Everything #includeable is rolled up herein...
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../mednafen-types.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/***
|
||||
*** dvdisaster.c
|
||||
|
@ -17,7 +17,12 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lec.h"
|
||||
|
||||
@ -38,39 +43,39 @@
|
||||
#define LEC_MODE2_FORM2_EDC_OFFSET 2348
|
||||
|
||||
|
||||
typedef uint8_t gf8_t;
|
||||
typedef u_int8_t gf8_t;
|
||||
|
||||
static uint8_t GF8_LOG[256];
|
||||
static u_int8_t GF8_LOG[256];
|
||||
static gf8_t GF8_ILOG[256];
|
||||
|
||||
static const class Gf8_Q_Coeffs_Results_01 {
|
||||
private:
|
||||
uint16_t table[43][256];
|
||||
u_int16_t table[43][256];
|
||||
public:
|
||||
Gf8_Q_Coeffs_Results_01();
|
||||
~Gf8_Q_Coeffs_Results_01() {}
|
||||
const uint16_t *operator[] (int i) const { return &table[i][0]; }
|
||||
operator const uint16_t *() const { return &table[0][0]; }
|
||||
const u_int16_t *operator[] (int i) const { return &table[i][0]; }
|
||||
operator const u_int16_t *() const { return &table[0][0]; }
|
||||
} CF8_Q_COEFFS_RESULTS_01;
|
||||
|
||||
static const class CrcTable {
|
||||
private:
|
||||
uint32_t table[256];
|
||||
u_int32_t table[256];
|
||||
public:
|
||||
CrcTable();
|
||||
~CrcTable() {}
|
||||
uint32_t operator[](int i) const { return table[i]; }
|
||||
operator const uint32_t *() const { return table; }
|
||||
u_int32_t operator[](int i) const { return table[i]; }
|
||||
operator const u_int32_t *() const { return table; }
|
||||
} CRCTABLE;
|
||||
|
||||
static const class ScrambleTable {
|
||||
private:
|
||||
uint8_t table[2340];
|
||||
u_int8_t table[2340];
|
||||
public:
|
||||
ScrambleTable();
|
||||
~ScrambleTable() {}
|
||||
uint8_t operator[](int i) const { return table[i]; }
|
||||
operator const uint8_t *() const { return table; }
|
||||
u_int8_t operator[](int i) const { return table[i]; }
|
||||
operator const u_int8_t *() const { return table; }
|
||||
} SCRAMBLE_TABLE;
|
||||
|
||||
/* Creates the logarithm and inverse logarithm table that is required
|
||||
@ -78,8 +83,8 @@ public:
|
||||
*/
|
||||
static void gf8_create_log_tables()
|
||||
{
|
||||
uint8_t log;
|
||||
uint16_t b;
|
||||
u_int8_t log;
|
||||
u_int16_t b;
|
||||
|
||||
for (b = 0; b <= 255; b++) {
|
||||
GF8_LOG[b] = 0;
|
||||
@ -89,8 +94,8 @@ static void gf8_create_log_tables()
|
||||
b = 1;
|
||||
|
||||
for (log = 0; log < 255; log++) {
|
||||
GF8_LOG[(uint8_t)b] = log;
|
||||
GF8_ILOG[log] = (uint8_t)b;
|
||||
GF8_LOG[(u_int8_t)b] = log;
|
||||
GF8_ILOG[log] = (u_int8_t)b;
|
||||
|
||||
b <<= 1;
|
||||
|
||||
@ -147,9 +152,9 @@ static gf8_t gf8_div(gf8_t a, gf8_t b)
|
||||
Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
|
||||
{
|
||||
int i, j;
|
||||
uint16_t c;
|
||||
u_int16_t c;
|
||||
gf8_t GF8_COEFFS_HELP[2][45];
|
||||
uint8_t GF8_Q_COEFFS[2][45];
|
||||
u_int8_t GF8_Q_COEFFS[2][45];
|
||||
|
||||
|
||||
gf8_create_log_tables();
|
||||
@ -220,10 +225,10 @@ Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
|
||||
|
||||
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
|
||||
*/
|
||||
static uint32_t mirror_bits(uint32_t d, int bits)
|
||||
static u_int32_t mirror_bits(u_int32_t d, int bits)
|
||||
{
|
||||
int i;
|
||||
uint32_t r = 0;
|
||||
u_int32_t r = 0;
|
||||
|
||||
for (i = 0; i < bits; i++) {
|
||||
r <<= 1;
|
||||
@ -243,8 +248,8 @@ static uint32_t mirror_bits(uint32_t d, int bits)
|
||||
*/
|
||||
CrcTable::CrcTable ()
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint32_t r;
|
||||
u_int32_t i, j;
|
||||
u_int32_t r;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
r = mirror_bits(i, 8);
|
||||
@ -270,9 +275,9 @@ CrcTable::CrcTable ()
|
||||
/* Calculates the CRC of given data with given lengths based on the
|
||||
* table lookup algorithm.
|
||||
*/
|
||||
static uint32_t calc_edc(uint8_t *data, int len)
|
||||
static u_int32_t calc_edc(u_int8_t *data, int len)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
u_int32_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
@ -286,9 +291,9 @@ static uint32_t calc_edc(uint8_t *data, int len)
|
||||
*/
|
||||
ScrambleTable::ScrambleTable()
|
||||
{
|
||||
uint16_t i, j;
|
||||
uint16_t reg = 1;
|
||||
uint8_t d;
|
||||
u_int16_t i, j;
|
||||
u_int16_t reg = 1;
|
||||
u_int8_t d;
|
||||
|
||||
for (i = 0; i < 2340; i++) {
|
||||
d = 0;
|
||||
@ -314,9 +319,9 @@ ScrambleTable::ScrambleTable()
|
||||
|
||||
/* Calc EDC for a MODE 1 sector
|
||||
*/
|
||||
static void calc_mode1_edc(uint8_t *sector)
|
||||
static void calc_mode1_edc(u_int8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
|
||||
u_int32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
|
||||
|
||||
sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
@ -326,9 +331,9 @@ static void calc_mode1_edc(uint8_t *sector)
|
||||
|
||||
/* Calc EDC for a XA form 1 sector
|
||||
*/
|
||||
static void calc_mode2_form1_edc(uint8_t *sector)
|
||||
static void calc_mode2_form1_edc(u_int8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM1_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL;
|
||||
@ -339,9 +344,9 @@ static void calc_mode2_form1_edc(uint8_t *sector)
|
||||
|
||||
/* Calc EDC for a XA form 2 sector
|
||||
*/
|
||||
static void calc_mode2_form2_edc(uint8_t *sector)
|
||||
static void calc_mode2_form2_edc(u_int8_t *sector)
|
||||
{
|
||||
uint32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM2_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL;
|
||||
@ -352,7 +357,7 @@ static void calc_mode2_form2_edc(uint8_t *sector)
|
||||
|
||||
/* Writes the sync pattern to the given sector.
|
||||
*/
|
||||
static void set_sync_pattern(uint8_t *sector)
|
||||
static void set_sync_pattern(u_int8_t *sector)
|
||||
{
|
||||
sector[0] = 0;
|
||||
|
||||
@ -363,14 +368,14 @@ static void set_sync_pattern(uint8_t *sector)
|
||||
}
|
||||
|
||||
|
||||
static uint8_t bin2bcd(uint8_t b)
|
||||
static u_int8_t bin2bcd(u_int8_t b)
|
||||
{
|
||||
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
|
||||
}
|
||||
|
||||
/* Builds the sector header.
|
||||
*/
|
||||
static void set_sector_header(uint8_t mode, uint32_t adr, uint8_t *sector)
|
||||
static void set_sector_header(u_int8_t mode, u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75));
|
||||
sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60);
|
||||
@ -381,14 +386,14 @@ static void set_sector_header(uint8_t mode, uint32_t adr, uint8_t *sector)
|
||||
/* Calculate the P parities for the sector.
|
||||
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
|
||||
*/
|
||||
static void calc_P_parity(uint8_t *sector)
|
||||
static void calc_P_parity(u_int8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t p01_msb, p01_lsb;
|
||||
uint8_t *p_lsb_start;
|
||||
uint8_t *p_lsb;
|
||||
uint8_t *p0, *p1;
|
||||
uint8_t d0,d1;
|
||||
u_int16_t p01_msb, p01_lsb;
|
||||
u_int8_t *p_lsb_start;
|
||||
u_int8_t *p_lsb;
|
||||
u_int8_t *p0, *p1;
|
||||
u_int8_t d0,d1;
|
||||
|
||||
p_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
@ -426,14 +431,14 @@ static void calc_P_parity(uint8_t *sector)
|
||||
/* Calculate the Q parities for the sector.
|
||||
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
|
||||
*/
|
||||
static void calc_Q_parity(uint8_t *sector)
|
||||
static void calc_Q_parity(u_int8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t q01_lsb, q01_msb;
|
||||
uint8_t *q_lsb_start;
|
||||
uint8_t *q_lsb;
|
||||
uint8_t *q0, *q1, *q_start;
|
||||
uint8_t d0,d1;
|
||||
u_int16_t q01_lsb, q01_msb;
|
||||
u_int8_t *q_lsb_start;
|
||||
u_int8_t *q_lsb;
|
||||
u_int8_t *q0, *q1, *q_start;
|
||||
u_int8_t d0,d1;
|
||||
|
||||
q_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
@ -477,9 +482,9 @@ static void calc_Q_parity(uint8_t *sector)
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(uint32_t adr, uint8_t *sector)
|
||||
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
uint16_t i;
|
||||
u_int16_t i;
|
||||
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(0, adr, sector);
|
||||
@ -495,7 +500,7 @@ void lec_encode_mode0_sector(uint32_t adr, uint8_t *sector)
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(uint32_t adr, uint8_t *sector)
|
||||
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(1, adr, sector);
|
||||
@ -521,7 +526,7 @@ void lec_encode_mode1_sector(uint32_t adr, uint8_t *sector)
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(uint32_t adr, uint8_t *sector)
|
||||
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(2, adr, sector);
|
||||
@ -532,7 +537,7 @@ void lec_encode_mode2_sector(uint32_t adr, uint8_t *sector)
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(uint32_t adr, uint8_t *sector)
|
||||
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
@ -556,7 +561,7 @@ void lec_encode_mode2_form1_sector(uint32_t adr, uint8_t *sector)
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector)
|
||||
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
@ -568,12 +573,12 @@ void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector)
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(uint8_t *sector)
|
||||
void lec_scramble(u_int8_t *sector)
|
||||
{
|
||||
uint16_t i;
|
||||
const uint8_t *stable = SCRAMBLE_TABLE;
|
||||
uint8_t *p = sector;
|
||||
uint8_t tmp;
|
||||
u_int16_t i;
|
||||
const u_int8_t *stable = SCRAMBLE_TABLE;
|
||||
u_int8_t *p = sector;
|
||||
u_int8_t tmp;
|
||||
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
@ -592,3 +597,95 @@ void lec_scramble(uint8_t *sector)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *infile;
|
||||
char *outfile;
|
||||
int fd_in, fd_out;
|
||||
u_int8_t buffer1[2352];
|
||||
u_int8_t buffer2[2352];
|
||||
u_int32_t lba;
|
||||
int i;
|
||||
|
||||
#if 0
|
||||
for (i = 0; i < 2048; i++)
|
||||
buffer1[i + 16] = 234;
|
||||
|
||||
lba = 150;
|
||||
|
||||
for (i = 0; i < 100000; i++) {
|
||||
lec_encode_mode1_sector(lba, buffer1);
|
||||
lec_scramble(buffer2);
|
||||
lba++;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (argc != 3)
|
||||
return 1;
|
||||
|
||||
infile = argv[1];
|
||||
outfile = argv[2];
|
||||
|
||||
|
||||
if ((fd_in = open(infile, O_RDONLY)) < 0) {
|
||||
perror("Cannot open input file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((fd_out = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
|
||||
perror("Cannot open output file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
lba = 150;
|
||||
|
||||
do {
|
||||
if (read(fd_in, buffer1, 2352) != 2352)
|
||||
break;
|
||||
|
||||
switch (*(buffer1 + 12 + 3)) {
|
||||
case 1:
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2048);
|
||||
|
||||
lec_encode_mode1_sector(lba, buffer2);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if ((*(buffer1 + 12 + 4 + 2) & 0x20) != 0) {
|
||||
/* form 2 sector */
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2324 + 8);
|
||||
lec_encode_mode2_form2_sector(lba, buffer2);
|
||||
}
|
||||
else {
|
||||
/* form 1 sector */
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2048 + 8);
|
||||
lec_encode_mode2_form1_sector(lba, buffer2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(buffer1, buffer2, 2352) != 0) {
|
||||
printf("Verify error at lba %ld\n", lba);
|
||||
}
|
||||
|
||||
lec_scramble(buffer2);
|
||||
write(fd_out, buffer2, 2352);
|
||||
|
||||
lba++;
|
||||
} while (1);
|
||||
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -24,7 +24,12 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef uint32_t u_int32_t;
|
||||
typedef uint16_t u_int16_t;
|
||||
typedef uint8_t u_int8_t;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
@ -34,39 +39,39 @@
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(uint32_t adr, uint8_t *sector);
|
||||
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a MODE 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(uint32_t adr, uint8_t *sector);
|
||||
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a MODE 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(uint32_t adr, uint8_t *sector);
|
||||
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a XA form 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(uint32_t adr, uint8_t *sector);
|
||||
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a XA form 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector);
|
||||
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(uint8_t *sector);
|
||||
void lec_scramble(u_int8_t *sector);
|
||||
|
||||
#endif
|
||||
|
@ -106,7 +106,7 @@ static int32 lastts;
|
||||
static int32 scsicd_ne = 0;
|
||||
|
||||
// ADPCM variables and whatnot
|
||||
//#define ADPCM_DEBUG(x, ...) { /*printf("[Half=%d, End=%d, Playing=%d] "x, ADPCM.HalfReached, ADPCM.EndReached, ADPCM.Playing, ## __VA_ARGS__);*/ }
|
||||
#define ADPCM_DEBUG(x, ...) { /*printf("[Half=%d, End=%d, Playing=%d] "x, ADPCM.HalfReached, ADPCM.EndReached, ADPCM.Playing, ## __VA_ARGS__);*/ }
|
||||
|
||||
typedef Blip_Synth<blip_good_quality, 4096> ADSynth;
|
||||
static ADSynth ADPCMSynth;
|
||||
@ -607,7 +607,7 @@ uint8 PCECD_Read(uint32 timestamp, uint32 A, int32 &next_event, const bool PeekM
|
||||
case 0xa:
|
||||
if(!PeekMode)
|
||||
{
|
||||
//ADPCM_DEBUG("ReadBuffer\n");
|
||||
ADPCM_DEBUG("ReadBuffer\n");
|
||||
ADPCM.ReadPending = 19 * 3; //24 * 3;
|
||||
}
|
||||
|
||||
@ -744,12 +744,12 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
ADPCM.Addr &= 0xFF00;
|
||||
ADPCM.Addr |= V;
|
||||
|
||||
//ADPCM_DEBUG("SAL: %02x, %d\n", V, timestamp);
|
||||
ADPCM_DEBUG("SAL: %02x, %d\n", V, timestamp);
|
||||
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(ADPCM.LastCmd & 0x10)
|
||||
{
|
||||
//ADPCM_DEBUG("Set length(crazy way L): %04x\n", ADPCM.Addr);
|
||||
ADPCM_DEBUG("Set length(crazy way L): %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
}
|
||||
break;
|
||||
@ -761,12 +761,12 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
ADPCM.Addr &= 0x00FF;
|
||||
ADPCM.Addr |= V << 8;
|
||||
|
||||
//ADPCM_DEBUG("SAH: %02x, %d\n", V, timestamp);
|
||||
ADPCM_DEBUG("SAH: %02x, %d\n", V, timestamp);
|
||||
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(ADPCM.LastCmd & 0x10)
|
||||
{
|
||||
//ADPCM_DEBUG("Set length(crazy way H): %04x\n", ADPCM.Addr);
|
||||
ADPCM_DEBUG("Set length(crazy way H): %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
}
|
||||
break;
|
||||
@ -778,7 +778,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
break;
|
||||
|
||||
case 0xb: // adpcm dma
|
||||
//ADPCM_DEBUG("DMA: %02x\n", V);
|
||||
ADPCM_DEBUG("DMA: %02x\n", V);
|
||||
_Port[0xb] = data;
|
||||
break;
|
||||
|
||||
@ -786,7 +786,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
//ADPCM_DEBUG("Write180D: %02x\n", V);
|
||||
ADPCM_DEBUG("Write180D: %02x\n", V);
|
||||
if(data & 0x80)
|
||||
{
|
||||
ADPCM.Addr = 0;
|
||||
@ -824,7 +824,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(data & 0x10)
|
||||
{
|
||||
//ADPCM_DEBUG("Set length: %04x\n", ADPCM.Addr);
|
||||
ADPCM_DEBUG("Set length: %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
ADPCM.EndReached = false;
|
||||
}
|
||||
@ -837,7 +837,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
else
|
||||
ADPCM.ReadAddr = (ADPCM.Addr - 1) & 0xFFFF;
|
||||
|
||||
//ADPCM_DEBUG("Set ReadAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.ReadAddr);
|
||||
ADPCM_DEBUG("Set ReadAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.ReadAddr);
|
||||
}
|
||||
|
||||
// D0 and D1 control write address
|
||||
@ -846,7 +846,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
ADPCM.WriteAddr = ADPCM.Addr;
|
||||
if(!(data & 0x1))
|
||||
ADPCM.WriteAddr = (ADPCM.WriteAddr - 1) & 0xFFFF;
|
||||
//ADPCM_DEBUG("Set WriteAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.WriteAddr);
|
||||
ADPCM_DEBUG("Set WriteAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.WriteAddr);
|
||||
}
|
||||
ADPCM.LastCmd = data;
|
||||
UpdateADPCMIRQState();
|
||||
@ -858,7 +858,7 @@ int32 PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
|
||||
ADPCM.SampleFreq = freq;
|
||||
|
||||
//ADPCM_DEBUG("Freq: %02x\n", freq);
|
||||
ADPCM_DEBUG("Freq: %02x\n", freq);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -10,8 +10,7 @@ static void DoNEC_PCE_SAPSP(const uint8 *cdb)
|
||||
//printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default:
|
||||
//SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
|
||||
default: SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
|
||||
case 0x00:
|
||||
new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
@ -87,8 +86,8 @@ static void DoNEC_PCE_SAPEP(const uint8 *cdb)
|
||||
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default:
|
||||
//SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
|
||||
default: SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
|
||||
|
||||
case 0x00:
|
||||
new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "cdromif.h"
|
||||
#include "SimpleFIFO.h"
|
||||
|
||||
//#define SCSIDBG(format, ...) { printf("SCSI: " format "\n", ## __VA_ARGS__); }
|
||||
#define SCSIDBG(format, ...) { printf("SCSI: " format "\n", ## __VA_ARGS__); }
|
||||
//#define SCSIDBG(format, ...) { }
|
||||
|
||||
using namespace CDUtility;
|
||||
@ -304,13 +304,11 @@ static void GenSubQFromSubPW(void)
|
||||
// printf("%02x ", SubQBuf[i]);
|
||||
//printf("\n");
|
||||
|
||||
#if 0
|
||||
if(!subq_check_checksum(SubQBuf))
|
||||
{
|
||||
SCSIDBG("SubQ checksum error!");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
memcpy(cd.SubQBuf_Last, SubQBuf, 0xC);
|
||||
|
||||
@ -629,66 +627,6 @@ static ModePage ModePages[] =
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
},
|
||||
|
||||
@ -697,69 +635,6 @@ static ModePage ModePages[] =
|
||||
0x01,
|
||||
{
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
},
|
||||
|
||||
@ -769,68 +644,6 @@ static ModePage ModePages[] =
|
||||
{
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x11, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
},
|
||||
|
||||
@ -839,69 +652,6 @@ static ModePage ModePages[] =
|
||||
0x01,
|
||||
{
|
||||
{ 0x00, 0x00, 0xFF },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
},
|
||||
|
||||
@ -925,56 +675,6 @@ static ModePage ModePages[] =
|
||||
{ 0x00, 0x00, 0x00 }, // Outport port 2 volume.
|
||||
{ 0x00, 0x00, 0x00 }, // Outport port 3 channel selection.
|
||||
{ 0x00, 0x00, 0x00 }, // Outport port 3 volume.
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -1155,7 +855,7 @@ static void DoMODESENSE6(const uint8 *cdb)
|
||||
uint8 PageMatchOR = 0x00;
|
||||
bool AnyPageMatch = false;
|
||||
|
||||
//SCSIDBG("Mode sense 6: %02x %d %d %d\n", PageCode, PC, DBD, AllocSize);
|
||||
SCSIDBG("Mode sense 6: %02x %d %d %d\n", PageCode, PC, DBD, AllocSize);
|
||||
|
||||
if(!AllocSize)
|
||||
{
|
||||
@ -1258,14 +958,14 @@ static void DoSTARTSTOPUNIT6(const uint8 *cdb)
|
||||
bool LoEj = cdb[4] & 0x02;
|
||||
bool Start = cdb[4] & 0x01;
|
||||
|
||||
//SCSIDBG("Do start stop unit 6: %d %d %d\n", Immed, LoEj, Start);
|
||||
SCSIDBG("Do start stop unit 6: %d %d %d\n", Immed, LoEj, Start);
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
|
||||
static void DoREZEROUNIT(const uint8 *cdb)
|
||||
{
|
||||
//SCSIDBG("Rezero Unit: %02x\n", cdb[5]);
|
||||
SCSIDBG("Rezero Unit: %02x\n", cdb[5]);
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
|
||||
@ -2165,14 +1865,12 @@ static void DoREADBase(uint32 sa, uint32 sc)
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(SCSILog)
|
||||
{
|
||||
int Track = toc.FindTrackByLBA(sa);
|
||||
uint32 Offset = sa - toc.tracks[Track].lba; //Cur_CDIF->GetTrackStartPositionLBA(Track);
|
||||
SCSILog("SCSI", "Read: start=0x%08x(track=%d, offs=0x%08x), cnt=0x%08x", sa, Track, Offset, sc);
|
||||
}
|
||||
#endif
|
||||
|
||||
SectorAddr = sa;
|
||||
SectorCount = sc;
|
||||
@ -2205,7 +1903,7 @@ static void DoREAD6(const uint8 *cdb)
|
||||
// TODO: confirm real PCE does this(PC-FX does at least).
|
||||
if(!sc)
|
||||
{
|
||||
//SCSIDBG("READ(6) with count == 0.\n");
|
||||
SCSIDBG("READ(6) with count == 0.\n");
|
||||
sc = 256;
|
||||
}
|
||||
|
||||
@ -2512,7 +2210,7 @@ static void DoNEC_SCAN(const uint8 *cdb)
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default:
|
||||
//SCSIDBG("Unknown NECSCAN format");
|
||||
SCSIDBG("Unknown NECSCAN format");
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
@ -2967,7 +2665,6 @@ uint32 SCSICD_Run(scsicd_timestamp_t system_timestamp)
|
||||
while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
|
||||
cmd_info_ptr++;
|
||||
|
||||
#if 0
|
||||
if(SCSILog)
|
||||
{
|
||||
char log_buffer[1024];
|
||||
@ -2981,33 +2678,28 @@ uint32 SCSICD_Run(scsicd_timestamp_t system_timestamp)
|
||||
for(int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++)
|
||||
lb_pos += trio_snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]);
|
||||
|
||||
//SCSILog("SCSI", "%s", log_buffer);
|
||||
SCSILog("SCSI", "%s", log_buffer);
|
||||
//puts(log_buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if(cmd_info_ptr->pretty_name == NULL) // Command not found!
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
|
||||
|
||||
//SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]);
|
||||
SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]);
|
||||
|
||||
#if 0
|
||||
if(SCSILog)
|
||||
SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]);
|
||||
#endif
|
||||
|
||||
cd.command_buffer_pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
if(cmd_info_ptr->flags & SCF_UNTESTED)
|
||||
{
|
||||
SCSIDBG("Untested SCSI command: %02x, %s", cd.command_buffer[0], cmd_info_ptr->pretty_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(cd.TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
|
||||
{
|
||||
|
@ -223,3 +223,62 @@ void MDFN_trim(char *string)
|
||||
MDFN_rtrim(string);
|
||||
MDFN_ltrim(string);
|
||||
}
|
||||
|
||||
// Remove whitespace from beginning of string
|
||||
void MDFN_ltrim(std::string &string)
|
||||
{
|
||||
size_t len = string.length();
|
||||
size_t di, si;
|
||||
bool InWhitespace = TRUE;
|
||||
|
||||
di = si = 0;
|
||||
|
||||
while(si < len)
|
||||
{
|
||||
if(InWhitespace && (string[si] == ' ' || string[si] == '\r' || string[si] == '\n' || string[si] == '\t' || string[si] == 0x0b))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
InWhitespace = FALSE;
|
||||
string[di] = string[si];
|
||||
di++;
|
||||
}
|
||||
si++;
|
||||
}
|
||||
|
||||
string.resize(di);
|
||||
}
|
||||
|
||||
// Remove whitespace from end of string
|
||||
void MDFN_rtrim(std::string &string)
|
||||
{
|
||||
size_t len = string.length();
|
||||
|
||||
if(len)
|
||||
{
|
||||
size_t x = len;
|
||||
size_t new_len = len;
|
||||
|
||||
do
|
||||
{
|
||||
x--;
|
||||
|
||||
if(!(string[x] == ' ' || string[x] == '\r' || string[x] == '\n' || string[x] == '\t' || string[x] == 0x0b))
|
||||
break;
|
||||
|
||||
new_len--;
|
||||
} while(x);
|
||||
|
||||
string.resize(new_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MDFN_trim(std::string &string)
|
||||
{
|
||||
MDFN_rtrim(string);
|
||||
MDFN_ltrim(string);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,10 @@ void MDFN_ltrim(char *string);
|
||||
void MDFN_rtrim(char *string);
|
||||
void MDFN_trim(char *string);
|
||||
|
||||
void MDFN_ltrim(std::string &string);
|
||||
void MDFN_rtrim(std::string &string);
|
||||
void MDFN_trim(std::string &string);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MDFNMKF_STATE = 0,
|
||||
|
@ -171,12 +171,12 @@ MDFNGI *MDFNI_LoadCD(const char *force_module, const char *devicename)
|
||||
|
||||
for(unsigned i = 0; i < file_list.size(); i++)
|
||||
{
|
||||
CDInterfaces.push_back(CDIF_Open(file_list[i].c_str(), false /* cdimage_memcache */));
|
||||
CDInterfaces.push_back(CDIF_Open(file_list[i].c_str(), false, false /* cdimage_memcache */));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CDInterfaces.push_back(CDIF_Open(devicename, false /* cdimage_memcache */));
|
||||
CDInterfaces.push_back(CDIF_Open(devicename, false, false /* cdimage_memcache */));
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
|
12
mednafen/psx/Makefile.am
Normal file
12
mednafen/psx/Makefile.am
Normal file
@ -0,0 +1,12 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MMX_CFLAGS@ @SSE_CFLAGS@ @SSE2_CFLAGS@ -funroll-loops
|
||||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/intl -I$(top_srcdir)
|
||||
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp mdec.cpp
|
||||
|
||||
libpsx_a_SOURCES += input/gamepad.cpp input/dualanalog.cpp input/dualshock.cpp input/memcard.cpp input/multitap.cpp input/mouse.cpp input/negcon.cpp input/guncon.cpp input/justifier.cpp
|
||||
|
||||
if WANT_DEBUGGER
|
||||
libpsx_a_SOURCES += debug.cpp
|
||||
endif
|
702
mednafen/psx/Makefile.in
Normal file
702
mednafen/psx/Makefile.in
Normal file
@ -0,0 +1,702 @@
|
||||
# Makefile.in generated by automake 1.11.6 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
|
||||
# Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
VPATH = @srcdir@
|
||||
am__make_dryrun = \
|
||||
{ \
|
||||
am__dry=no; \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
|
||||
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
|
||||
*) \
|
||||
for am__flg in $$MAKEFLAGS; do \
|
||||
case $$am__flg in \
|
||||
*=*|--*) ;; \
|
||||
*n*) am__dry=yes; break;; \
|
||||
esac; \
|
||||
done;; \
|
||||
esac; \
|
||||
test $$am__dry = yes; \
|
||||
}
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
@WANT_DEBUGGER_TRUE@am__append_1 = debug.cpp
|
||||
subdir = src/psx
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \
|
||||
$(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/fcntl-o.m4 \
|
||||
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glibc2.m4 \
|
||||
$(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
|
||||
$(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intl.m4 \
|
||||
$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intmax.m4 \
|
||||
$(top_srcdir)/m4/inttypes-pri.m4 \
|
||||
$(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lcmessage.m4 \
|
||||
$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
|
||||
$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
|
||||
$(top_srcdir)/m4/lock.m4 $(top_srcdir)/m4/longlong.m4 \
|
||||
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
|
||||
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
|
||||
$(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
|
||||
$(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \
|
||||
$(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/stdint_h.m4 \
|
||||
$(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/uintmax_t.m4 \
|
||||
$(top_srcdir)/m4/visibility.m4 $(top_srcdir)/m4/wchar_t.m4 \
|
||||
$(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xsize.m4 \
|
||||
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
||||
CONFIG_HEADER = $(top_builddir)/include/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
LIBRARIES = $(noinst_LIBRARIES)
|
||||
ARFLAGS = cru
|
||||
AM_V_AR = $(am__v_AR_@AM_V@)
|
||||
am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
libpsx_a_AR = $(AR) $(ARFLAGS)
|
||||
libpsx_a_LIBADD =
|
||||
am__libpsx_a_SOURCES_DIST = psx.cpp irq.cpp timer.cpp dma.cpp \
|
||||
frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp \
|
||||
gpu.cpp mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp debug.cpp
|
||||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
@WANT_DEBUGGER_TRUE@am__objects_1 = debug.$(OBJEXT)
|
||||
am_libpsx_a_OBJECTS = psx.$(OBJEXT) irq.$(OBJEXT) timer.$(OBJEXT) \
|
||||
dma.$(OBJEXT) frontio.$(OBJEXT) sio.$(OBJEXT) cpu.$(OBJEXT) \
|
||||
gte.$(OBJEXT) dis.$(OBJEXT) cdc.$(OBJEXT) spu.$(OBJEXT) \
|
||||
gpu.$(OBJEXT) mdec.$(OBJEXT) input/gamepad.$(OBJEXT) \
|
||||
input/dualanalog.$(OBJEXT) input/dualshock.$(OBJEXT) \
|
||||
input/memcard.$(OBJEXT) input/multitap.$(OBJEXT) \
|
||||
input/mouse.$(OBJEXT) input/negcon.$(OBJEXT) \
|
||||
input/guncon.$(OBJEXT) input/justifier.$(OBJEXT) \
|
||||
$(am__objects_1)
|
||||
libpsx_a_OBJECTS = $(am_libpsx_a_OBJECTS)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
am__mv = mv -f
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
AM_V_lt = $(am__v_lt_@AM_V@)
|
||||
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
|
||||
am__v_lt_0 = --silent
|
||||
LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
AM_V_CXX = $(am__v_CXX_@AM_V@)
|
||||
am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
|
||||
am__v_CXX_0 = @echo " CXX " $@;
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
|
||||
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
|
||||
am__v_CXXLD_0 = @echo " CXXLD " $@;
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
SOURCES = $(libpsx_a_SOURCES)
|
||||
DIST_SOURCES = $(am__libpsx_a_SOURCES_DIST)
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ALLOCA = @ALLOCA@
|
||||
ALSA_CFLAGS = @ALSA_CFLAGS@
|
||||
ALSA_LIBS = @ALSA_LIBS@
|
||||
AMTAR = @AMTAR@
|
||||
AM_CFLAGS = @AM_CFLAGS@
|
||||
AM_CXXFLAGS = @AM_CXXFLAGS@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
|
||||
CATOBJEXT = @CATOBJEXT@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASDEPMODE = @CCASDEPMODE@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DATADIRNAME = @DATADIRNAME@
|
||||
DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MMX_CFLAGS@ @SSE_CFLAGS@ @SSE2_CFLAGS@ -funroll-loops
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
GBA_EXTRA_FLAGS = @GBA_EXTRA_FLAGS@
|
||||
GENCAT = @GENCAT@
|
||||
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
|
||||
GLIBC2 = @GLIBC2@
|
||||
GLIBC21 = @GLIBC21@
|
||||
GMSGFMT = @GMSGFMT@
|
||||
GMSGFMT_015 = @GMSGFMT_015@
|
||||
GREP = @GREP@
|
||||
HAVE_ASPRINTF = @HAVE_ASPRINTF@
|
||||
HAVE_NEWLOCALE = @HAVE_NEWLOCALE@
|
||||
HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
|
||||
HAVE_SNPRINTF = @HAVE_SNPRINTF@
|
||||
HAVE_VISIBILITY = @HAVE_VISIBILITY@
|
||||
HAVE_WPRINTF = @HAVE_WPRINTF@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
INSTOBJEXT = @INSTOBJEXT@
|
||||
INTLBISON = @INTLBISON@
|
||||
INTLLIBS = @INTLLIBS@
|
||||
INTLOBJS = @INTLOBJS@
|
||||
INTL_DEFAULT_VERBOSITY = @INTL_DEFAULT_VERBOSITY@
|
||||
INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
|
||||
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
|
||||
JACK_CFLAGS = @JACK_CFLAGS@
|
||||
JACK_LIBS = @JACK_LIBS@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBCDIO_CFLAGS = @LIBCDIO_CFLAGS@
|
||||
LIBCDIO_LIBS = @LIBCDIO_LIBS@
|
||||
LIBICONV = @LIBICONV@
|
||||
LIBINTL = @LIBINTL@
|
||||
LIBMULTITHREAD = @LIBMULTITHREAD@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBPTH = @LIBPTH@
|
||||
LIBPTH_PREFIX = @LIBPTH_PREFIX@
|
||||
LIBS = @LIBS@
|
||||
LIBTHREAD = @LIBTHREAD@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBC = @LTLIBC@
|
||||
LTLIBICONV = @LTLIBICONV@
|
||||
LTLIBINTL = @LTLIBINTL@
|
||||
LTLIBMULTITHREAD = @LTLIBMULTITHREAD@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
LTLIBPTH = @LTLIBPTH@
|
||||
LTLIBTHREAD = @LTLIBTHREAD@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
MMX_CFLAGS = @MMX_CFLAGS@
|
||||
MSGFMT = @MSGFMT@
|
||||
MSGFMT_015 = @MSGFMT_015@
|
||||
MSGMERGE = @MSGMERGE@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
POSUB = @POSUB@
|
||||
PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@
|
||||
RANLIB = @RANLIB@
|
||||
SDL_CFLAGS = @SDL_CFLAGS@
|
||||
SDL_CONFIG = @SDL_CONFIG@
|
||||
SDL_LIBS = @SDL_LIBS@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
|
||||
SNDFILE_LIBS = @SNDFILE_LIBS@
|
||||
SNES_EXTRA_CXXFLAGS = @SNES_EXTRA_CXXFLAGS@
|
||||
SNES_EXTRA_FLAGS = @SNES_EXTRA_FLAGS@
|
||||
SNES_PERF_EXTRA_CXXFLAGS = @SNES_PERF_EXTRA_CXXFLAGS@
|
||||
SNES_PERF_EXTRA_FLAGS = @SNES_PERF_EXTRA_FLAGS@
|
||||
SSE2_CFLAGS = @SSE2_CFLAGS@
|
||||
SSE3_CFLAGS = @SSE3_CFLAGS@
|
||||
SSE_CFLAGS = @SSE_CFLAGS@
|
||||
STRIP = @STRIP@
|
||||
TRIO_CFLAGS = @TRIO_CFLAGS@
|
||||
USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
|
||||
USE_NLS = @USE_NLS@
|
||||
VERSION = @VERSION@
|
||||
WARNING_FLAGS = @WARNING_FLAGS@
|
||||
WINDRES = @WINDRES@
|
||||
WOE32 = @WOE32@
|
||||
WOE32DLL = @WOE32DLL@
|
||||
XGETTEXT = @XGETTEXT@
|
||||
XGETTEXT_015 = @XGETTEXT_015@
|
||||
XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
|
||||
XMKMF = @XMKMF@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/intl -I$(top_srcdir)
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp \
|
||||
sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp \
|
||||
mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp $(am__append_1)
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/psx/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu src/psx/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
clean-noinstLIBRARIES:
|
||||
-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
|
||||
input/$(am__dirstamp):
|
||||
@$(MKDIR_P) input
|
||||
@: > input/$(am__dirstamp)
|
||||
input/$(DEPDIR)/$(am__dirstamp):
|
||||
@$(MKDIR_P) input/$(DEPDIR)
|
||||
@: > input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/gamepad.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/dualanalog.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/dualshock.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/memcard.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/multitap.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/mouse.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/negcon.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/guncon.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/justifier.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
libpsx.a: $(libpsx_a_OBJECTS) $(libpsx_a_DEPENDENCIES) $(EXTRA_libpsx_a_DEPENDENCIES)
|
||||
$(AM_V_at)-rm -f libpsx.a
|
||||
$(AM_V_AR)$(libpsx_a_AR) libpsx.a $(libpsx_a_OBJECTS) $(libpsx_a_LIBADD)
|
||||
$(AM_V_at)$(RANLIB) libpsx.a
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
-rm -f input/dualanalog.$(OBJEXT)
|
||||
-rm -f input/dualshock.$(OBJEXT)
|
||||
-rm -f input/gamepad.$(OBJEXT)
|
||||
-rm -f input/guncon.$(OBJEXT)
|
||||
-rm -f input/justifier.$(OBJEXT)
|
||||
-rm -f input/memcard.$(OBJEXT)
|
||||
-rm -f input/mouse.$(OBJEXT)
|
||||
-rm -f input/multitap.$(OBJEXT)
|
||||
-rm -f input/negcon.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dis.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dma.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frontio.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gte.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irq.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdec.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psx.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/dualanalog.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/dualshock.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/gamepad.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/guncon.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/justifier.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/memcard.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/mouse.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/multitap.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/negcon.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cpp.lo:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LIBRARIES)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
-rm -f input/$(DEPDIR)/$(am__dirstamp)
|
||||
-rm -f input/$(am__dirstamp)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR) input/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR) input/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLIBRARIES ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
3
mednafen/psx/NOTES
Normal file
3
mednafen/psx/NOTES
Normal file
@ -0,0 +1,3 @@
|
||||
Capcom games(MMX4, MMX5, etc) - Sensitive about CD access and XA timing(XA playback problems).
|
||||
|
||||
Viewpoint - Extra-sensitive about GPU LL DMA timing? (It was generating exceptions for some timings...)
|
153
mednafen/psx/PSX-TODO
Normal file
153
mednafen/psx/PSX-TODO
Normal file
@ -0,0 +1,153 @@
|
||||
***Major issues***
|
||||
|
||||
Tomb Raider(PAL)
|
||||
(reported, and confirmed) "was fighting Tomb Raider PAL this night, when you get out of pool in Lara's home the game
|
||||
bugs out: issues GetlocP, waits for it to complete and expects seek to be NOT complete (i.e. older location)"
|
||||
Thoughts on emulating:
|
||||
CalcSpinupTime()
|
||||
DS_SPINUP
|
||||
StatusAfterSpinup ORRR we could scrap that StatusAfter* idea and have some kind of fancy function callback system...
|
||||
|
||||
Monkey Hero
|
||||
Locks up shortly after title screen. It's straddling a framebuffer write command's parameters across two linked-list
|
||||
blocks(2 words at the end of the first block, and 1 word at the beginning of the next block). AFAIK, this construct will only
|
||||
work on the real thing under *very* strict timing constraints. Probably explains why it reportedly doesn't work on the PS2.
|
||||
It's as easy to fix as changing a certain 2 to a 3 in the GPU command table, and probably won't break anything,
|
||||
but I'm still reluctant to do it...
|
||||
|
||||
Tales of Destiny (Japanese version 1.0)
|
||||
Frequently locks up after random battles in the cave after Mary joins. US version is reported to not suffer from this problem.
|
||||
Looks like the game is stuck waiting for an SPU IRQ that never happens. The problem seems to be related to usage of Mary's "Moushuuken" skill.
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
***Medium issues***
|
||||
|
||||
Championship Motocross 2001
|
||||
Invisible driver. GHOSTRIDER~ The palette data in RAM is apparently being selectively zero'd out between
|
||||
the DMA from CDC and the DMA to GPU for some reason. Timing issue related maybe? (Probably CPU instruction timing issue)
|
||||
|
||||
Nicktoons Racing
|
||||
Totally fubared sound effects. Likely CPU instruction timing-related(decreasing the IPC fixes this problem), or perhaps timing
|
||||
related to SPU RAM writes?
|
||||
|
||||
-----------------------------------------------------------------------------------------------
|
||||
|
||||
***Lesser issues***
|
||||
|
||||
Castrol Honda Superbike Racing
|
||||
Graphical glitches on startup, menus(fixed now probably), and loading screens. (What is with all
|
||||
these racing games having weird timing-related problems, it's like some sort of curse)
|
||||
|
||||
Vigilante 8
|
||||
Has weird red and green(and a few blue) garbage pixels in patches sometimes during gameplay; "garbage" pixel placement
|
||||
is somewhat sparse, so it might not even be noticeable if it were to occur on a PS1 with composite or S-video output, due to limited
|
||||
chroma bandwidth.
|
||||
|
||||
Final Fantasy 7
|
||||
"Sony Computer blah blah" game startup image is a bit glitchy; interlacing-related glitch. That part is really sensitive to CPU, GPU, DMA,
|
||||
and RAM timing...
|
||||
|
||||
Adventures of Lomax, The
|
||||
"QSOUND" loading screen during startup is a flickery mess; interlacing-related glitch from the looks of it.
|
||||
Maybe it's like that on an actual PS1 too?
|
||||
|
||||
Nightmare Creatures
|
||||
BIOS reverb sound excessively contaminates beginning of game.
|
||||
|
||||
Crusaders of Might and Magic
|
||||
The CD-XA buffering increase for ToD II is apparently exacerbating the early voice cutoff problem in this game.
|
||||
|
||||
Shadow Master
|
||||
Might have broken startup images.
|
||||
|
||||
WipeOut 3
|
||||
Music stops playing after a long while; maybe it's supposed to do that?
|
||||
|
||||
-----------------------------------------------------------------------------------------------
|
||||
|
||||
***Chaotic timing issues(listed here for reference and testing after any future timing tweaks or improvements)***
|
||||
|
||||
Battle Arena Toshinden
|
||||
NBA Jam Extreme
|
||||
Zero Divide
|
||||
Run too fast if CPU and(/or?) GPU operations execute too fast. Probably not an issue anymore, but still
|
||||
listed here for reference.
|
||||
|
||||
Ballblazer Champions
|
||||
Excessively sensitive to timing issues; might lock up during startup sometimes.
|
||||
|
||||
FIFA - Road to Worldcup 98 (USA)
|
||||
Used to lock up during startup; probably CPU instruction timing-related, or MDEC timing related,
|
||||
or CDC timing related, or DMA timing related, or maybe the moon phase is to blame!
|
||||
|
||||
[CDC] WARNING: Interrupting command 0x02, phase=0, timeleft=1195 with command=0x02
|
||||
[CDC] Bad number(5) of args(first check) for command 0x02
|
||||
0x39 0x23 0x22 0x41 0x67
|
||||
|
||||
Freestyle Motocross: McGrath vs. Pastrana
|
||||
Used to suffer from random fairly long(but finite) freezes during gameplay. Possibly CPU instruction-timing
|
||||
related. Possibly CDC-timing related.
|
||||
|
||||
[CDC] Command: ReadN ---
|
||||
[CDC] Command: Standby ---
|
||||
[CDC] Command: Nop ---
|
||||
[CDC] Command: Setmode --- 0xa0
|
||||
[CDC] Command: Setloc --- 0x08 0x13 0x53
|
||||
[CDC] Command: ReadN ---
|
||||
20076
|
||||
[CDC] Command: Standby ---
|
||||
[CDC] Command: Nop ---
|
||||
[CDC] Command: Setmode --- 0xa0
|
||||
[CDC] Command: Setloc --- 0x08 0x13 0x61
|
||||
0x800668B8
|
||||
|
||||
Resident Evil 2
|
||||
Lockup on second disc in a certain place; probably related to seek timing. Seems to be fixed now, but still listed
|
||||
here for reference.
|
||||
|
||||
|
||||
-----------------------------------------------------------------------------------------------
|
||||
|
||||
Star Wars Dark Forces notes(for working towards removing a kludge/hack in the DMA IRQ handling)
|
||||
PC=0x8001B124: [DMA] Write: 1f8010f4 008a0000, DMAIntStatus=0000000c
|
||||
if(V == 0x008a0000 && DMAIntStatus == 0x0000000c)
|
||||
DBG_Break(); // V |= 8 << 24; //V |= 4 << 24;
|
||||
|
||||
Ensure(long-term, IE a note to the FUTURE) that input device state is latched in independent variables at the start of the
|
||||
input device's handling of a state read command, so that when games are reading device state across an MDFNI_Emulate() boundary, the
|
||||
data won't change in the middle, which could hypothetically cause problems in some rare circumstances(and make entering
|
||||
button combos in games more finicky).
|
||||
|
||||
Investigate CD-XA FIR filter on a PS1, for 4-bit and 8-bit modes, and both playback rates. Might be difficult without a modchip,
|
||||
though...
|
||||
|
||||
Respect device(GPU, SPU, etc.) DMA mode/memory access mode bits(more for homebrew's sake than commercial games').
|
||||
|
||||
Test if Dual Analog vibration compatibility mode can be restored with DualShock after using DualShock extended features if the "Analog"
|
||||
mode button on the gamepad is pressed after the extended features are used.
|
||||
|
||||
Test time delta between GPU LL DMA end and GPU non-busy status for various primitive types in sequence on a PS1.
|
||||
|
||||
Test IRQ and COP0 latencies; PSX IRQ controller latency, software IRQ bit latency, latency of both relevant COP0 IRQ enable bits.
|
||||
|
||||
Test IRQ with LWC2.
|
||||
|
||||
Test IRQ with COP0 instructions(interrupted execution or not?).
|
||||
|
||||
Determine maximum quad height and width. See if drawing is an all-or-nothing thing, or if one triangle of the pair will still be drawn
|
||||
if it's not too large.
|
||||
|
||||
Test 0x0 and 1x1 polygon throughput for all triangle and quad drawing commands.
|
||||
|
||||
Fix line drawing algorithm to match test results on the real thing.
|
||||
|
||||
|
||||
Test time between voice on and envelope reset; test time between voice on and first ADPCM block memory fetch.
|
||||
|
||||
The SPU in the PS1 might sometimes drop voice-on events when playing an ADPCM block that loops to itself(and was also the first and only
|
||||
ADPCM block, at least in the test program I noticed the issue in); investigate further.
|
||||
|
||||
|
||||
|
||||
Make sure debugger COPn disassembly is correct(no typos or whatnot).
|
2
mednafen/psx/README.NOW
Normal file
2
mednafen/psx/README.NOW
Normal file
@ -0,0 +1,2 @@
|
||||
This PSX emulation code is horribly unfinished! Please don't take and use any pieces of it in any other project unless you have
|
||||
a very good reason to!
|
38
mednafen/psx/SOURCES
Normal file
38
mednafen/psx/SOURCES
Normal file
@ -0,0 +1,38 @@
|
||||
Sources of helpful information used in PS1 emulation(though some of it, especially older compiled information,
|
||||
is wrong to some degree or incomplete, but it's still useful):
|
||||
|
||||
PCSX(and derivatives/forks)
|
||||
MAME/MESS
|
||||
P.E.Op.S
|
||||
|
||||
Blade Lib
|
||||
psxsdk
|
||||
|
||||
doomed's PSX documents
|
||||
bITmASTER's document
|
||||
Neill Corlett's SPU documents
|
||||
T.Fujita's SIO documents
|
||||
ChrlyMac
|
||||
Exophase
|
||||
mizvekov
|
||||
notaz
|
||||
pSXAuthor
|
||||
smf(blog)
|
||||
shalma(forum posts, changelogs)
|
||||
drhell's site
|
||||
jac's CD-XA document
|
||||
"The PlayStation 1 Video (STR) Format" - M. Sabin
|
||||
Various PS1 emulator compatibility lists(for identifying problematic games)
|
||||
|
||||
|
||||
MIPS RISC Architecture - Gerry Kane (1st and 2nd editions)
|
||||
MIPS Programmer's Handbook, The
|
||||
|
||||
-----------
|
||||
General(non-PS1-specific) CD/CDROM information and code:
|
||||
|
||||
SCSI-3 Multimedia Commands Revision 10A
|
||||
ECMA-130
|
||||
|
||||
cdrdao
|
||||
dvdisaster
|
2202
mednafen/psx/cdc.cpp
Normal file
2202
mednafen/psx/cdc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
317
mednafen/psx/cdc.h
Normal file
317
mednafen/psx/cdc.h
Normal file
@ -0,0 +1,317 @@
|
||||
#ifndef __MDFN_PSX_CDC_H
|
||||
#define __MDFN_PSX_CDC_H
|
||||
|
||||
#include "../cdrom/cdromif.h"
|
||||
#include "../cdrom/SimpleFIFO.h"
|
||||
#include "../clamp.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
struct CD_Audio_Buffer
|
||||
{
|
||||
int16 Samples[2][0x1000]; // [0][...] = l, [1][...] = r
|
||||
int32 Size;
|
||||
uint32 Freq;
|
||||
};
|
||||
|
||||
class PS_CDC
|
||||
{
|
||||
public:
|
||||
|
||||
PS_CDC();
|
||||
~PS_CDC();
|
||||
|
||||
void SetDisc(bool tray_open, CDIF *cdif, const char disc_id[4]);
|
||||
|
||||
void Power(void);
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
void ResetTS(void);
|
||||
|
||||
int32 CalcNextEvent(void); // Returns in master cycles to next event.
|
||||
|
||||
pscpu_timestamp_t Update(const pscpu_timestamp_t timestamp);
|
||||
|
||||
void Write(const pscpu_timestamp_t timestamp, uint32 A, uint8 V);
|
||||
uint8 Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
bool DMACanRead(void);
|
||||
uint32 DMARead(void);
|
||||
|
||||
INLINE uint32 GetCDAudioFreq(void)
|
||||
{
|
||||
if(AudioBuffer_UsedCount && !AudioBuffer_InPrebuffer)
|
||||
{
|
||||
const unsigned wb = AudioBuffer_ReadPos >> 12;
|
||||
return AudioBuffer[wb].Freq;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
inline void ApplyVolume(int32 samples[2])
|
||||
{
|
||||
int32 left_source = samples[0]; //(int16)MDFN_de16lsb(&buf[i * sizeof(int16) * 2 + 0]);
|
||||
int32 right_source = samples[1]; //(int16)MDFN_de16lsb(&buf[i * sizeof(int16) * 2 + 2]);
|
||||
int32 left_out = ((left_source * DecodeVolume[0][0]) >> 7) + ((right_source * DecodeVolume[1][0]) >> 7);
|
||||
int32 right_out = ((left_source * DecodeVolume[0][1]) >> 7) + ((right_source * DecodeVolume[1][1]) >> 7);
|
||||
|
||||
clamp(&left_out, -32768, 32767);
|
||||
clamp(&right_out, -32768, 32767);
|
||||
|
||||
if(Muted)
|
||||
{
|
||||
left_out = right_out = 0;
|
||||
}
|
||||
|
||||
samples[0] = left_out;
|
||||
samples[1] = right_out;
|
||||
}
|
||||
public:
|
||||
|
||||
INLINE void GetCDAudio(int32 &l, int32 &r)
|
||||
{
|
||||
if(AudioBuffer_UsedCount && !AudioBuffer_InPrebuffer)
|
||||
{
|
||||
const unsigned wb = AudioBuffer_ReadPos >> 12;
|
||||
int32 samples[2] = { AudioBuffer[wb].Samples[0][AudioBuffer_ReadPos & 0xFFF], AudioBuffer[wb].Samples[1][AudioBuffer_ReadPos & 0xFFF] };
|
||||
|
||||
ApplyVolume(samples);
|
||||
|
||||
l = samples[0];
|
||||
r = samples[1];
|
||||
|
||||
AudioBuffer_ReadPos = ((AudioBuffer_ReadPos + 1) & 0xFFF) | (AudioBuffer_ReadPos & ~0xFFF);
|
||||
|
||||
if((AudioBuffer_ReadPos & 0xFFF) == (AudioBuffer[wb].Size & 0xFFF))
|
||||
{
|
||||
//printf("RP == size; usedcount(predec)= %d, PSRCounter=%d\n", AudioBuffer_UsedCount, PSRCounter);
|
||||
AudioBuffer_ReadPos = ((((AudioBuffer_ReadPos >> 12) + 1) % AudioBuffer_Count) << 12);
|
||||
AudioBuffer_UsedCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void SoftReset(void);
|
||||
|
||||
CDIF *Cur_CDIF;
|
||||
bool DiscChanged;
|
||||
int32 DiscStartupDelay;
|
||||
|
||||
|
||||
enum { AudioBuffer_PreBufferCount = 2 };
|
||||
enum { AudioBuffer_Count = 4 };
|
||||
|
||||
CD_Audio_Buffer AudioBuffer[AudioBuffer_Count];
|
||||
uint32 AudioBuffer_ReadPos;
|
||||
uint32 AudioBuffer_WritePos;
|
||||
uint32 AudioBuffer_UsedCount;
|
||||
bool AudioBuffer_InPrebuffer;
|
||||
|
||||
uint8 Pending_DecodeVolume[2][2], DecodeVolume[2][2]; // [data_source][output_port]
|
||||
|
||||
void ClearAudioBuffers(void);
|
||||
|
||||
uint8 RegSelector;
|
||||
uint8 ArgsBuf[16];
|
||||
uint8 ArgsWP; // 5-bit(0 ... 31)
|
||||
uint8 ArgsRP; // 5-bit(0 ... 31)
|
||||
|
||||
uint8 ArgsReceiveLatch;
|
||||
uint8 ArgsReceiveBuf[32];
|
||||
uint8 ArgsReceiveIn;
|
||||
|
||||
uint8 ResultsBuffer[16];
|
||||
uint8 ResultsIn; // 5-bit(0 ... 31)
|
||||
uint8 ResultsWP; // Write position, 4 bit(0 ... 15).
|
||||
uint8 ResultsRP; // Read position, 4 bit(0 ... 15).
|
||||
|
||||
SimpleFIFO<uint8> DMABuffer;
|
||||
uint8 SB[2340];
|
||||
uint32 SB_In;
|
||||
|
||||
|
||||
uint8 SubQBuf[0xC];
|
||||
uint8 SubQBuf_Safe[0xC];
|
||||
bool SubQChecksumOK;
|
||||
|
||||
bool HeaderBufValid;
|
||||
uint8 HeaderBuf[12];
|
||||
|
||||
void RecalcIRQ(void);
|
||||
enum
|
||||
{
|
||||
CDCIRQ_NONE = 0,
|
||||
CDCIRQ_DATA_READY = 1,
|
||||
CDCIRQ_COMPLETE = 2,
|
||||
CDCIRQ_ACKNOWLEDGE = 3,
|
||||
CDCIRQ_DATA_END = 4,
|
||||
CDCIRQ_DISC_ERROR = 5
|
||||
};
|
||||
|
||||
// Names are just guessed for these based on what conditions cause them:
|
||||
enum
|
||||
{
|
||||
ERRCODE_BAD_ARGVAL = 0x10,
|
||||
ERRCODE_BAD_NUMARGS = 0x20,
|
||||
ERRCODE_BAD_COMMAND = 0x40,
|
||||
ERRCODE_NOT_READY = 0x80, // 0x80 (happens with getlocl when drive isn't reading, pause when tray is open, and MAYBE when trying to run an async
|
||||
// command while another async command is currently in its asynch phase being executed[pause when in readtoc, todo test more])
|
||||
};
|
||||
|
||||
uint8 IRQBuffer;
|
||||
uint8 IRQOutTestMask;
|
||||
int32 CDCReadyReceiveCounter; // IRQBuffer being non-zero prevents new results and new IRQ from coming in and erasing the current results,
|
||||
// but apparently at least one CONFOUNDED game is clearing the IRQ state BEFORE reading the results, so we need to have a delay
|
||||
// between IRQBuffer being cleared to when we allow new results to come in. (The real thing should be like this too,
|
||||
// but the mechanism is probably more nuanced and complex and ugly and I like anchovy pizza)
|
||||
|
||||
void BeginResults(void);
|
||||
void WriteIRQ(uint8);
|
||||
void WriteResult(uint8);
|
||||
uint8 ReadResult(void);
|
||||
|
||||
uint8 FilterFile;
|
||||
uint8 FilterChan;
|
||||
|
||||
|
||||
uint8 PendingCommand;
|
||||
int PendingCommandPhase;
|
||||
int32 PendingCommandCounter;
|
||||
|
||||
int32 SPUCounter;
|
||||
|
||||
enum { MODE_SPEED = 0x80 };
|
||||
enum { MODE_STRSND = 0x40 };
|
||||
enum { MODE_SIZE = 0x20 };
|
||||
enum { MODE_SIZE2 = 0x10 };
|
||||
enum { MODE_SF = 0x08 };
|
||||
enum { MODE_REPORT = 0x04 };
|
||||
enum { MODE_AUTOPAUSE = 0x02 };
|
||||
enum { MODE_CDDA = 0x01 };
|
||||
uint8 Mode;
|
||||
|
||||
enum
|
||||
{
|
||||
DS_STANDBY = -2,
|
||||
DS_PAUSED = -1,
|
||||
DS_STOPPED = 0,
|
||||
DS_SEEKING,
|
||||
DS_SEEKING_LOGICAL,
|
||||
DS_PLAY_SEEKING,
|
||||
DS_PLAYING,
|
||||
DS_READING,
|
||||
DS_RESETTING
|
||||
};
|
||||
int DriveStatus;
|
||||
int StatusAfterSeek;
|
||||
bool Forward;
|
||||
bool Backward;
|
||||
bool Muted;
|
||||
|
||||
int32 PlayTrackMatch;
|
||||
|
||||
int32 PSRCounter;
|
||||
|
||||
int32 CurSector;
|
||||
|
||||
unsigned AsyncIRQPending;
|
||||
uint8 AsyncResultsPending[16];
|
||||
uint8 AsyncResultsPendingCount;
|
||||
|
||||
int32 CalcSeekTime(int32 initial, int32 target, bool motor_on, bool paused);
|
||||
|
||||
void ClearAIP(void);
|
||||
void CheckAIP(void);
|
||||
void SetAIP(unsigned irq, unsigned result_count, uint8 *r);
|
||||
void SetAIP(unsigned irq, uint8 result0);
|
||||
void SetAIP(unsigned irq, uint8 result0, uint8 result1);
|
||||
|
||||
int32 SeekTarget;
|
||||
|
||||
pscpu_timestamp_t lastts;
|
||||
|
||||
CDUtility::TOC toc;
|
||||
bool IsPSXDisc;
|
||||
uint8 DiscID[4];
|
||||
|
||||
int32 CommandLoc;
|
||||
bool CommandLoc_Dirty;
|
||||
|
||||
uint8 MakeStatus(bool cmd_error = false);
|
||||
bool DecodeSubQ(uint8 *subpw);
|
||||
bool CommandCheckDiscPresent(void);
|
||||
|
||||
void EnbufferizeCDDASector(const uint8 *buf);
|
||||
bool XA_Test(const uint8 *sdata);
|
||||
void XA_ProcessSector(const uint8 *sdata, CD_Audio_Buffer *ab);
|
||||
int16 xa_previous[2][2];
|
||||
bool xa_cur_set;
|
||||
uint8 xa_cur_file;
|
||||
uint8 xa_cur_chan;
|
||||
|
||||
struct CDC_CTEntry
|
||||
{
|
||||
uint8 args_min;
|
||||
uint8 args_max;
|
||||
const char *name;
|
||||
int32 (PS_CDC::*func)(const int arg_count, const uint8 *args);
|
||||
int32 (PS_CDC::*func2)(void);
|
||||
};
|
||||
|
||||
void BeginSeek(uint32 target);
|
||||
void PreSeekHack(bool logical, uint32 target);
|
||||
void ReadBase(void);
|
||||
|
||||
static CDC_CTEntry Commands[0x20];
|
||||
|
||||
int32 Command_Sync(const int arg_count, const uint8 *args);
|
||||
int32 Command_Nop(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setloc(const int arg_count, const uint8 *args);
|
||||
int32 Command_Play(const int arg_count, const uint8 *args);
|
||||
int32 Command_Forward(const int arg_count, const uint8 *args);
|
||||
int32 Command_Backward(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadN(const int arg_count, const uint8 *args);
|
||||
int32 Command_Standby(const int arg_count, const uint8 *args);
|
||||
int32 Command_Standby_Part2(void);
|
||||
int32 Command_Stop(const int arg_count, const uint8 *args);
|
||||
int32 Command_Stop_Part2(void);
|
||||
int32 Command_Pause(const int arg_count, const uint8 *args);
|
||||
int32 Command_Pause_Part2(void);
|
||||
int32 Command_Reset(const int arg_count, const uint8 *args);
|
||||
int32 Command_Mute(const int arg_count, const uint8 *args);
|
||||
int32 Command_Demute(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setfilter(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setmode(const int arg_count, const uint8 *args);
|
||||
int32 Command_Getparam(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetlocL(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetlocP(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ReadT(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadT_Part2(void);
|
||||
|
||||
int32 Command_GetTN(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetTD(const int arg_count, const uint8 *args);
|
||||
int32 Command_SeekL(const int arg_count, const uint8 *args);
|
||||
int32 Command_SeekP(const int arg_count, const uint8 *args);
|
||||
int32 Command_Seek_PartN(void);
|
||||
|
||||
int32 Command_Test(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ID(const int arg_count, const uint8 *args);
|
||||
int32 Command_ID_Part2(void);
|
||||
|
||||
int32 Command_ReadS(const int arg_count, const uint8 *args);
|
||||
int32 Command_Init(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ReadTOC(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadTOC_Part2(void);
|
||||
|
||||
int32 Command_0x1d(const int arg_count, const uint8 *args);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
2568
mednafen/psx/cpu.cpp
Normal file
2568
mednafen/psx/cpu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
254
mednafen/psx/cpu.h
Normal file
254
mednafen/psx/cpu.h
Normal file
@ -0,0 +1,254 @@
|
||||
#ifndef __MDFN_PSX_CPU_H
|
||||
#define __MDFN_PSX_CPU_H
|
||||
|
||||
/*
|
||||
Load delay notes:
|
||||
|
||||
// Takes 1 less
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"or %0, %1, %1\n\t"
|
||||
|
||||
// cycle than this:
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"or %0, %1, %1\n\t"
|
||||
"nop\n\t"
|
||||
|
||||
|
||||
// Both of these
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"or %1, %0, %0\n\t"
|
||||
|
||||
// take same...(which is kind of odd).
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"or %1, %0, %0\n\t"
|
||||
"nop\n\t"
|
||||
*/
|
||||
|
||||
#include "gte.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
#define PS_CPU_EMULATE_ICACHE 1
|
||||
|
||||
class PS_CPU
|
||||
{
|
||||
public:
|
||||
|
||||
PS_CPU();
|
||||
~PS_CPU();
|
||||
|
||||
// FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes
|
||||
// will always be multiples of 4.
|
||||
enum { FAST_MAP_SHIFT = 16 };
|
||||
enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT };
|
||||
|
||||
void SetFastMap(void *region_mem, uint32 region_address, uint32 region_size);
|
||||
|
||||
INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg)
|
||||
{
|
||||
next_event_ts = next_event_ts_arg;
|
||||
}
|
||||
|
||||
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool ILHMode);
|
||||
|
||||
void Power(void);
|
||||
|
||||
// which ranges 0-5, inclusive
|
||||
void AssertIRQ(int which, bool asserted);
|
||||
|
||||
void SetHalt(bool status);
|
||||
|
||||
// TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed.
|
||||
void SetBIU(uint32 val);
|
||||
uint32 GetBIU(void);
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
private:
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 GPR[32];
|
||||
uint32 GPR_dummy; // Used in load delay simulation(indexing past the end of GPR)
|
||||
};
|
||||
uint32 LO;
|
||||
uint32 HI;
|
||||
|
||||
|
||||
uint32 BACKED_PC;
|
||||
uint32 BACKED_new_PC;
|
||||
uint32 BACKED_new_PC_mask;
|
||||
|
||||
uint32 IPCache;
|
||||
void RecalcIPCache(void);
|
||||
bool Halted;
|
||||
|
||||
uint32 BACKED_LDWhich;
|
||||
uint32 BACKED_LDValue;
|
||||
uint32 LDAbsorb;
|
||||
|
||||
pscpu_timestamp_t next_event_ts;
|
||||
pscpu_timestamp_t gte_ts_done;
|
||||
pscpu_timestamp_t muldiv_ts_done;
|
||||
|
||||
uint32 BIU;
|
||||
|
||||
struct __ICache
|
||||
{
|
||||
uint32 TV;
|
||||
uint32 Data;
|
||||
} ICache[1024];
|
||||
|
||||
enum
|
||||
{
|
||||
CP0REG_BPC = 3, // PC breakpoint address.
|
||||
CP0REG_BDA = 5, // Data load/store breakpoint address.
|
||||
CP0REG_TAR = 6, // Target address(???)
|
||||
CP0REG_DCIC = 7, // Cache control
|
||||
CP0REG_BDAM = 9, // Data load/store address mask.
|
||||
CP0REG_BPCM = 11, // PC breakpoint address mask.
|
||||
CP0REG_SR = 12,
|
||||
CP0REG_CAUSE = 13,
|
||||
CP0REG_EPC = 14,
|
||||
CP0REG_PRID = 15, // Product ID
|
||||
CP0REG_ERREG = 16
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32 Regs[32];
|
||||
struct
|
||||
{
|
||||
uint32 Unused00;
|
||||
uint32 Unused01;
|
||||
uint32 Unused02;
|
||||
uint32 BPC; // RW
|
||||
uint32 Unused04;
|
||||
uint32 BDA; // RW
|
||||
uint32 TAR;
|
||||
uint32 DCIC; // RW
|
||||
uint32 Unused08;
|
||||
uint32 BDAM; // R/W
|
||||
uint32 Unused0A;
|
||||
uint32 BPCM; // R/W
|
||||
uint32 SR; // R/W
|
||||
uint32 CAUSE; // R/W(partial)
|
||||
uint32 EPC; // R
|
||||
uint32 PRID; // R
|
||||
uint32 ERREG; // ?(may not exist, test)
|
||||
};
|
||||
};
|
||||
} CP0;
|
||||
|
||||
#if 1
|
||||
//uint32 WrAbsorb;
|
||||
//uint8 WrAbsorbShift;
|
||||
|
||||
// On read:
|
||||
//WrAbsorb = 0;
|
||||
//WrAbsorbShift = 0;
|
||||
|
||||
// On write:
|
||||
//WrAbsorb >>= (WrAbsorbShift >> 2) & 8;
|
||||
//WrAbsorbShift -= (WrAbsorbShift >> 2) & 8;
|
||||
|
||||
//WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift;
|
||||
//WrAbsorbShift += 8;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 ReadAbsorb[0x20];
|
||||
uint8 ReadAbsorbDummy;
|
||||
};
|
||||
uint8 ReadAbsorbWhich;
|
||||
uint8 ReadFudge;
|
||||
|
||||
//uint32 WriteAbsorb;
|
||||
//uint8 WriteAbsorbCount;
|
||||
//uint8 WriteAbsorbMonkey;
|
||||
|
||||
MultiAccessSizeMem<1024, uint32, false> ScratchRAM;
|
||||
|
||||
//PS_GTE GTE;
|
||||
|
||||
uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)];
|
||||
uint8 DummyPage[FAST_MAP_PSIZE];
|
||||
|
||||
enum
|
||||
{
|
||||
EXCEPTION_INT = 0,
|
||||
EXCEPTION_MOD = 1,
|
||||
EXCEPTION_TLBL = 2,
|
||||
EXCEPTION_TLBS = 3,
|
||||
EXCEPTION_ADEL = 4, // Address error on load
|
||||
EXCEPTION_ADES = 5, // Address error on store
|
||||
EXCEPTION_IBE = 6, // Instruction bus error
|
||||
EXCEPTION_DBE = 7, // Data bus error
|
||||
EXCEPTION_SYSCALL = 8, // System call
|
||||
EXCEPTION_BP = 9, // Breakpoint
|
||||
EXCEPTION_RI = 10, // Reserved instruction
|
||||
EXCEPTION_COPU = 11, // Coprocessor unusable
|
||||
EXCEPTION_OV = 12 // Arithmetic overflow
|
||||
};
|
||||
|
||||
uint32 Exception(uint32 code, uint32 PC, const uint32 NPM) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
template<bool DebugMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in);
|
||||
|
||||
template<typename T> T PeekMemory(uint32 address) MDFN_COLD;
|
||||
template<typename T> T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false);
|
||||
template<typename T> void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false);
|
||||
|
||||
|
||||
//
|
||||
// Mednafen debugger stuff follows:
|
||||
//
|
||||
public:
|
||||
void SetCPUHook(void (*cpuh)(uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception));
|
||||
void CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr);
|
||||
|
||||
enum
|
||||
{
|
||||
GSREG_GPR = 0,
|
||||
GSREG_PC = 32,
|
||||
GSREG_PC_NEXT,
|
||||
GSREG_IN_BD_SLOT,
|
||||
GSREG_LO,
|
||||
GSREG_HI,
|
||||
GSREG_SR,
|
||||
GSREG_CAUSE,
|
||||
GSREG_EPC,
|
||||
};
|
||||
|
||||
uint32 GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||
void SetRegister(unsigned int which, uint32 value);
|
||||
bool PeekCheckICache(uint32 PC, uint32 *iw);
|
||||
uint8 PeekMem8(uint32 A);
|
||||
uint16 PeekMem16(uint32 A);
|
||||
uint32 PeekMem32(uint32 A);
|
||||
private:
|
||||
void (*CPUHook)(uint32 pc);
|
||||
void (*ADDBT)(uint32 from, uint32 to, bool exception);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
676
mednafen/psx/debug.cpp
Normal file
676
mednafen/psx/debug.cpp
Normal file
@ -0,0 +1,676 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "timer.h"
|
||||
#include "cdc.h"
|
||||
#include "spu.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
extern PS_GPU *GPU;
|
||||
extern PS_SPU *SPU;
|
||||
|
||||
static void (*CPUHook)(uint32) = NULL;
|
||||
static void (*BPCallB)(uint32 PC) = NULL;
|
||||
|
||||
struct PSX_BPOINT
|
||||
{
|
||||
uint32 A[2];
|
||||
int type;
|
||||
};
|
||||
|
||||
static std::vector<PSX_BPOINT> BreakPointsPC, BreakPointsRead, BreakPointsWrite;
|
||||
static bool FoundBPoint;
|
||||
|
||||
static int BTIndex = 0;
|
||||
|
||||
struct BTEntry
|
||||
{
|
||||
uint32 from;
|
||||
uint32 to;
|
||||
uint32 branch_count;
|
||||
bool exception;
|
||||
};
|
||||
|
||||
#define NUMBT 24
|
||||
static BTEntry BTEntries[NUMBT];
|
||||
|
||||
void DBG_Break(void)
|
||||
{
|
||||
FoundBPoint = true;
|
||||
}
|
||||
|
||||
static void AddBranchTrace(uint32 from, uint32 to, bool exception)
|
||||
{
|
||||
BTEntry *prevbt = &BTEntries[(BTIndex + NUMBT - 1) % NUMBT];
|
||||
|
||||
//if(BTEntries[(BTIndex - 1) & 0xF] == PC) return;
|
||||
|
||||
if(prevbt->from == from && prevbt->to == to && prevbt->exception == exception && prevbt->branch_count < 0xFFFFFFFF)
|
||||
prevbt->branch_count++;
|
||||
else
|
||||
{
|
||||
BTEntries[BTIndex].from = from;
|
||||
BTEntries[BTIndex].to = to;
|
||||
BTEntries[BTIndex].exception = exception;
|
||||
BTEntries[BTIndex].branch_count = 1;
|
||||
|
||||
BTIndex = (BTIndex + 1) % NUMBT;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<BranchTraceResult> GetBranchTrace(void)
|
||||
{
|
||||
BranchTraceResult tmp;
|
||||
std::vector<BranchTraceResult> ret;
|
||||
|
||||
for(int x = 0; x < NUMBT; x++)
|
||||
{
|
||||
const BTEntry *bt = &BTEntries[(x + BTIndex) % NUMBT];
|
||||
|
||||
tmp.count = bt->branch_count;
|
||||
trio_snprintf(tmp.from, sizeof(tmp.from), "%08x", bt->from);
|
||||
trio_snprintf(tmp.to, sizeof(tmp.to), "%08x", bt->to);
|
||||
trio_snprintf(tmp.code, sizeof(tmp.code), "%s", bt->exception ? "e" : "");
|
||||
|
||||
ret.push_back(tmp);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void CheckCPUBPCallB(bool write, uint32 address, unsigned int len)
|
||||
{
|
||||
std::vector<PSX_BPOINT>::iterator bpit;
|
||||
std::vector<PSX_BPOINT>::iterator bpit_end;
|
||||
|
||||
if(write)
|
||||
{
|
||||
bpit = BreakPointsWrite.begin();
|
||||
bpit_end = BreakPointsWrite.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
bpit = BreakPointsRead.begin();
|
||||
bpit_end = BreakPointsRead.end();
|
||||
}
|
||||
|
||||
while(bpit != bpit_end)
|
||||
{
|
||||
if(address >= bpit->A[0] && address <= bpit->A[1])
|
||||
{
|
||||
FoundBPoint = true;
|
||||
break;
|
||||
}
|
||||
bpit++;
|
||||
}
|
||||
}
|
||||
|
||||
static void CPUHandler(uint32 PC)
|
||||
{
|
||||
std::vector<PSX_BPOINT>::iterator bpit;
|
||||
|
||||
if(PC == 0xB0 && CPU->GetRegister(PS_CPU::GSREG_GPR + 9, NULL, 0) == 0x3D)
|
||||
{
|
||||
putchar(CPU->GetRegister(PS_CPU::GSREG_GPR + 4, NULL, 0));
|
||||
//exit(1);
|
||||
//puts((const char *)&MainRAM[CPU->GetRegister(PS_CPU::GSREG_GPR + 4, NULL, 0) & 0x1FFFFF]);
|
||||
}
|
||||
|
||||
|
||||
// FIXME/TODO: Call ForceEventUpdates() somewhere
|
||||
|
||||
for(bpit = BreakPointsPC.begin(); bpit != BreakPointsPC.end(); bpit++)
|
||||
{
|
||||
if(PC >= bpit->A[0] && PC <= bpit->A[1])
|
||||
{
|
||||
FoundBPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CPU->CheckBreakpoints(CheckCPUBPCallB, CPU->PeekMem32(PC));
|
||||
|
||||
if(FoundBPoint)
|
||||
{
|
||||
BPCallB(PC);
|
||||
FoundBPoint = 0;
|
||||
}
|
||||
|
||||
if(CPUHook)
|
||||
CPUHook(PC);
|
||||
}
|
||||
|
||||
|
||||
static void RedoCPUHook(void)
|
||||
{
|
||||
const bool HappyTest = BreakPointsPC.size() || BreakPointsRead.size() || BreakPointsWrite.size();
|
||||
void (*cpuh)(uint32);
|
||||
|
||||
cpuh = HappyTest ? CPUHandler : CPUHook;
|
||||
|
||||
CPU->SetCPUHook(cpuh, cpuh ? AddBranchTrace : NULL);
|
||||
}
|
||||
|
||||
static void FlushBreakPoints(int type)
|
||||
{
|
||||
if(type == BPOINT_READ)
|
||||
BreakPointsRead.clear();
|
||||
else if(type == BPOINT_WRITE)
|
||||
BreakPointsWrite.clear();
|
||||
else if(type == BPOINT_PC)
|
||||
BreakPointsPC.clear();
|
||||
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void AddBreakPoint(int type, unsigned int A1, unsigned int A2, bool logical)
|
||||
{
|
||||
PSX_BPOINT tmp;
|
||||
|
||||
tmp.A[0] = A1;
|
||||
tmp.A[1] = A2;
|
||||
tmp.type = type;
|
||||
|
||||
if(type == BPOINT_READ)
|
||||
BreakPointsRead.push_back(tmp);
|
||||
else if(type == BPOINT_WRITE)
|
||||
BreakPointsWrite.push_back(tmp);
|
||||
else if(type == BPOINT_PC)
|
||||
BreakPointsPC.push_back(tmp);
|
||||
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void SetCPUCallback(void (*callb)(uint32 PC))
|
||||
{
|
||||
CPUHook = callb;
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void SetBPCallback(void (*callb)(uint32 PC))
|
||||
{
|
||||
BPCallB = callb;
|
||||
}
|
||||
|
||||
|
||||
static void GetAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint8 *Buffer)
|
||||
{
|
||||
if(!strcmp(name, "cpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFFFFF;
|
||||
*Buffer = CPU->PeekMem8(Address);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "ram"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x1FFFFF;
|
||||
*Buffer = CPU->PeekMem8(Address);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "spu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x7FFFF;
|
||||
*Buffer = SPU->PeekSPURAM(Address >> 1) >> ((Address & 1) * 8);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "gpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFF;
|
||||
*Buffer = GPU->PeekRAM(Address >> 1) >> ((Address & 1) * 8);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void PutAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint32 Granularity, bool hl, const uint8 *Buffer)
|
||||
{
|
||||
if(!strcmp(name, "cpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
pscpu_timestamp_t dummy = 0;
|
||||
Address &= 0xFFFFFFFF;
|
||||
|
||||
// TODO
|
||||
PSX_MemWrite8(dummy, Address, *Buffer);
|
||||
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "gpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFF;
|
||||
|
||||
uint16 peeko = GPU->PeekRAM(Address >> 1);
|
||||
|
||||
GPU->PokeRAM(Address >> 1, (*Buffer << ((Address & 1) * 8)) | (peeko & (0xFF00 >> ((Address & 1) * 8))) );
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "spu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x7FFFF;
|
||||
|
||||
uint16 peeko = SPU->PeekSPURAM(Address >> 1);
|
||||
|
||||
SPU->PokeSPURAM(Address >> 1, (*Buffer << ((Address & 1) * 8)) | (peeko & (0xFF00 >> ((Address & 1) * 8))) );
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static uint32 MemPeek(uint32 A, unsigned int bsize, bool hl, bool logical)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
for(unsigned int i = 0; i < bsize; i++)
|
||||
ret |= CPU->PeekMem8(A + i) << (i * 8);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void Disassemble(uint32 &A, uint32 SpecialA, char *TextBuf)
|
||||
{
|
||||
assert(!(A & 0x3));
|
||||
|
||||
uint32 instr = CPU->PeekMem32(A);
|
||||
|
||||
CPU->PeekCheckICache(A, &instr);
|
||||
|
||||
strncpy(TextBuf, DisassembleMIPS(A, instr).c_str(), 256);
|
||||
TextBuf[255] = 0;
|
||||
|
||||
// trio_snprintf(TextBuf, 256, "0x%08x", instr);
|
||||
|
||||
A += 4;
|
||||
}
|
||||
|
||||
static MDFN_Surface *GfxDecode_Buf = NULL;
|
||||
static int GfxDecode_Line = -1;
|
||||
static int GfxDecode_Layer = 0;
|
||||
static int GfxDecode_Scroll = 0;
|
||||
static int GfxDecode_PBN = 0;
|
||||
|
||||
static void DoGfxDecode(void)
|
||||
{
|
||||
unsigned tp_w, tp_h;
|
||||
|
||||
tp_w = 256;
|
||||
tp_h = 256;
|
||||
|
||||
if(GfxDecode_Buf)
|
||||
{
|
||||
for(int sy = 0; sy < GfxDecode_Buf->h; sy++)
|
||||
{
|
||||
for(int sx = 0; sx < GfxDecode_Buf->w; sx++)
|
||||
{
|
||||
unsigned fb_x = ((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) & 1023;
|
||||
unsigned fb_y = (((sy + GfxDecode_Scroll) % GfxDecode_Buf->w) + ((((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) / 1024) * 256)) & 511;
|
||||
|
||||
uint16 pixel = GPU->PeekRAM(fb_y * 1024 + fb_x);
|
||||
|
||||
GfxDecode_Buf->pixels[(sy * GfxDecode_Buf->w * 3) + sx] = GfxDecode_Buf->MakeColor(((pixel >> 0) & 0x1F) * 255 / 31,
|
||||
((pixel >> 5) & 0x1F) * 255 / 31,
|
||||
((pixel >> 10) & 0x1F) * 255 / 31, 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DBG_GPUScanlineHook(unsigned scanline)
|
||||
{
|
||||
if((int)scanline == GfxDecode_Line)
|
||||
{
|
||||
DoGfxDecode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SetGraphicsDecode(MDFN_Surface *surface, int line, int which, int xscroll, int yscroll, int pbn)
|
||||
{
|
||||
GfxDecode_Buf = surface;
|
||||
GfxDecode_Line = line;
|
||||
GfxDecode_Layer = which;
|
||||
GfxDecode_Scroll = yscroll;
|
||||
GfxDecode_PBN = pbn;
|
||||
|
||||
if(GfxDecode_Buf && GfxDecode_Line == -1)
|
||||
DoGfxDecode();
|
||||
}
|
||||
|
||||
DebuggerInfoStruct PSX_DBGInfo =
|
||||
{
|
||||
"shift_jis",
|
||||
4, // Max instruction byte size
|
||||
4, // Instruction alignment(bytes)
|
||||
32, // Logical address bits
|
||||
32, // Physical address bits
|
||||
0x00000000, // Default watch addr
|
||||
~0U, // ZP addr
|
||||
|
||||
MemPeek,
|
||||
Disassemble,
|
||||
NULL,
|
||||
NULL, //ForceIRQ,
|
||||
NULL, //NESDBG_GetVector,
|
||||
FlushBreakPoints,
|
||||
AddBreakPoint,
|
||||
SetCPUCallback,
|
||||
SetBPCallback,
|
||||
GetBranchTrace,
|
||||
SetGraphicsDecode,
|
||||
NULL, //PCFXDBG_SetLogFunc,
|
||||
};
|
||||
|
||||
static RegType Regs_Misc[] =
|
||||
{
|
||||
{ TIMER_GSREG_COUNTER0, "COUNTER0", "Counter 0", 2 },
|
||||
{ TIMER_GSREG_MODE0, "MODE0", "Mode 0", 2 },
|
||||
{ TIMER_GSREG_TARGET0, "TARGET0", "Target 0", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
|
||||
{ TIMER_GSREG_COUNTER1, "COUNTER1", "Counter 1", 2 },
|
||||
{ TIMER_GSREG_MODE1, "MODE1", "Mode 1", 2 },
|
||||
{ TIMER_GSREG_TARGET1, "TARGET1", "Target 1", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ TIMER_GSREG_COUNTER2, "COUNTER2", "Counter 2", 2 },
|
||||
{ TIMER_GSREG_MODE2, "MODE2", "Mode 2", 2 },
|
||||
{ TIMER_GSREG_TARGET2, "TARGET2", "Target 2", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ 0x10000 | IRQ_GSREG_ASSERTED, "ASSERTED", "IRQ Asserted", 2 },
|
||||
{ 0x10000 | IRQ_GSREG_STATUS, "STATUS", "IRQ Status", 2 },
|
||||
{ 0x10000 | IRQ_GSREG_MASK, "MASK", "IRQ Mask", 2 },
|
||||
|
||||
{ 0, "", "", 0 }
|
||||
};
|
||||
|
||||
|
||||
static uint32 GetRegister_Misc(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
if(id & 0x10000)
|
||||
return(IRQ_GetRegister(id & 0xFFFF, special, special_len));
|
||||
else
|
||||
return(TIMER_GetRegister(id & 0xFFFF, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_Misc(const unsigned int id, uint32 value)
|
||||
{
|
||||
if(id & 0x10000)
|
||||
IRQ_SetRegister(id & 0xFFFF, value);
|
||||
else
|
||||
TIMER_SetRegister(id & 0xFFFF, value);
|
||||
}
|
||||
|
||||
static RegGroupType MiscRegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_Misc,
|
||||
GetRegister_Misc,
|
||||
SetRegister_Misc
|
||||
};
|
||||
|
||||
static RegType Regs_SPU[] =
|
||||
{
|
||||
{ PS_SPU::GSREG_SPUCONTROL, "SPUCTRL", "SPU Control", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_FM_ON, "FMOn", "FM Enable", 3 },
|
||||
{ PS_SPU::GSREG_NOISE_ON, "NoiseOn", "Noise Enable", 3 },
|
||||
{ PS_SPU::GSREG_REVERB_ON, "ReverbOn", "Reverb Enable", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_CDVOL_L, "CDVolL", "CD Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_CDVOL_R, "CDVolR", "CD Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_L, "DryVolCL", "Dry Volume Control Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_R, "DryVolCR", "Dry Volume Control Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_L, "DryVolL", "Dry Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_R, "DryVolR", "Dry Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_WETVOL_L, "WetVolL", "Wet Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_WETVOL_R, "WetVolR", "Wet Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_RWADDR, "RWAddr", "SPURAM Read/Write Address", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_IRQADDR, "IRQAddr", "IRQ Compare Address", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_REVERBWA, "ReverbWA", "Reverb Work Area(Raw)", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_VOICEON, "VoiceOn", "Voice On", 3 },
|
||||
{ PS_SPU::GSREG_VOICEOFF, "VoiceOff", "Voice Off", 3 },
|
||||
{ PS_SPU::GSREG_BLOCKEND, "BlockEnd", "Block End", 3 },
|
||||
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ PS_SPU::GSREG_FB_SRC_A, "FB_SRC_A", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_SRC_B, "FB_SRC_B", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_ALPHA, "IIR_ALPHA", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_A, "ACC_COEF_A", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_B, "ACC_COEF_B", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_C, "ACC_COEF_C", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_D, "ACC_COEF_D", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_COEF, "IIR_COEF", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_ALPHA, "FB_ALPHA", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_X, "FB_X", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_A0, "IIR_DEST_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_A1, "IIR_DEST_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_A0, "ACC_SRC_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_A1, "ACC_SRC_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_B0, "ACC_SRC_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_B1, "ACC_SRC_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_A0, "IIR_SRC_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_A1, "IIR_SRC_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_B0, "IIR_DEST_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_B1, "IIR_DEST_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_C0, "ACC_SRC_C0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_C1, "ACC_SRC_C1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_D0, "ACC_SRC_D0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_D1, "ACC_SRC_D1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_B1, "IIR_SRC_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_B0, "IIR_SRC_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_A0, "MIX_DEST_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_A1, "MIX_DEST_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_B0, "MIX_DEST_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_B1, "MIX_DEST_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IN_COEF_L, "IN_COEF_L", "", 2 },
|
||||
{ PS_SPU::GSREG_IN_COEF_R, "IN_COEF_R", "", 2 },
|
||||
|
||||
{ 0, "", "", 0 },
|
||||
};
|
||||
|
||||
#define VOICE_HELPER(v) \
|
||||
{ 0, "--V"#v"--", "", 0xFFFF }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_CTRL_L + v * 256, "VolCL", "Volume Control Left", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_CTRL_R + v * 256, "VolCR", "Volume Control Right", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_L + v * 256, "VolL", "Volume Left", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_R + v * 256, "VolR", "Volume Right", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_PITCH + v * 256, "Pitch", "Pitch", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_STARTADDR + v * 256, "SAddr", "Start Address", 3 }, \
|
||||
{ PS_SPU:: GSREG_V0_ADSR_CTRL + v * 256, "ADSRCTRL", "ADSR Control", 4 }, \
|
||||
{ PS_SPU:: GSREG_V0_ADSR_LEVEL + v * 256, "ADSRLev", "ADSR Level", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_LOOP_ADDR + v * 256, "LAddr", "Loop Address", 3 }, \
|
||||
{ PS_SPU:: GSREG_V0_READ_ADDR + v * 256, "RAddr", "Read Address", 3 }
|
||||
|
||||
|
||||
static RegType Regs_SPU_Voices[] =
|
||||
{
|
||||
#if 1
|
||||
VOICE_HELPER(0),
|
||||
VOICE_HELPER(1),
|
||||
VOICE_HELPER(2),
|
||||
VOICE_HELPER(3),
|
||||
#else
|
||||
VOICE_HELPER(9),
|
||||
VOICE_HELPER(12),
|
||||
VOICE_HELPER(17),
|
||||
VOICE_HELPER(22),
|
||||
|
||||
//VOICE_HELPER(20),
|
||||
//VOICE_HELPER(21),
|
||||
//VOICE_HELPER(22),
|
||||
//VOICE_HELPER(23),
|
||||
#endif
|
||||
{ 0, "", "", 0 },
|
||||
};
|
||||
|
||||
|
||||
static uint32 GetRegister_SPU(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
return(SPU->GetRegister(id, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_SPU(const unsigned int id, uint32 value)
|
||||
{
|
||||
SPU->SetRegister(id, value);
|
||||
}
|
||||
|
||||
static RegGroupType SPURegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_SPU,
|
||||
GetRegister_SPU,
|
||||
SetRegister_SPU
|
||||
};
|
||||
|
||||
|
||||
static RegGroupType SPUVoicesRegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_SPU_Voices,
|
||||
GetRegister_SPU,
|
||||
SetRegister_SPU
|
||||
};
|
||||
|
||||
static RegType Regs_CPU[] =
|
||||
{
|
||||
{ PS_CPU::GSREG_PC, "PC", "PC", 4 },
|
||||
{ PS_CPU::GSREG_PC_NEXT, "NPC", "Next PC", 4 },
|
||||
{ PS_CPU::GSREG_IN_BD_SLOT, "INBD", "In Branch Delay Slot", 1 },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
{ PS_CPU::GSREG_GPR + 1, "at", "Assembler Temporary", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 2, "v0", "Return Value 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 3, "v1", "Return Value 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 4, "a0", "Argument 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 5, "a1", "Argument 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 6, "a2", "Argument 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 7, "a3", "Argument 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 8, "t0", "Temporary 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 9, "t1", "Temporary 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 10, "t2", "Temporary 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 11, "t3", "Temporary 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 12, "t4", "Temporary 4", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 13, "t5", "Temporary 5", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 14, "t6", "Temporary 6", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 15, "t7", "Temporary 7", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 16, "s0", "Subroutine Reg Var 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 17, "s1", "Subroutine Reg Var 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 18, "s2", "Subroutine Reg Var 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 19, "s3", "Subroutine Reg Var 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 20, "s4", "Subroutine Reg Var 4", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 21, "s5", "Subroutine Reg Var 5", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 22, "s6", "Subroutine Reg Var 6", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 23, "s7", "Subroutine Reg Var 7", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 24, "t8", "Temporary 8", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 25, "t9", "Temporary 9", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 26, "k0", "Interrupt/Trap Handler Reg 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 27, "k1", "Interrupt/Trap Handler Reg 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 28, "gp", "Global Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 29, "sp", "Stack Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 30, "s8", "Subroutine Reg Var 8/Frame Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 31, "ra", "Return Address", 4 },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ PS_CPU::GSREG_SR, "SR", "Status Register", 4 },
|
||||
{ PS_CPU::GSREG_CAUSE, "CAU","Cause Register", 4 },
|
||||
{ PS_CPU::GSREG_EPC, "EPC", "EPC Register", 4 },
|
||||
{ 0, "", "", 0 }
|
||||
};
|
||||
|
||||
static uint32 GetRegister_CPU(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
return(CPU->GetRegister(id, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_CPU(const unsigned int id, uint32 value)
|
||||
{
|
||||
CPU->SetRegister(id, value);
|
||||
}
|
||||
|
||||
static RegGroupType CPURegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_CPU,
|
||||
GetRegister_CPU,
|
||||
SetRegister_CPU
|
||||
};
|
||||
|
||||
|
||||
bool DBG_Init(void)
|
||||
{
|
||||
CPUHook = NULL;
|
||||
BPCallB = NULL;
|
||||
FoundBPoint = false;
|
||||
|
||||
MDFNDBG_AddRegGroup(&CPURegsGroup);
|
||||
MDFNDBG_AddRegGroup(&MiscRegsGroup);
|
||||
MDFNDBG_AddRegGroup(&SPURegsGroup);
|
||||
MDFNDBG_AddRegGroup(&SPUVoicesRegsGroup);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "cpu", "CPU Physical", 32);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "ram", "CPU Main Ram", 21);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "spu", "SPU RAM", 19);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "gpu", "GPU RAM", 20);
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
21
mednafen/psx/debug.h
Normal file
21
mednafen/psx/debug.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef __MDFN_PSX_DEBUG_H
|
||||
#define __MDFN_PSX_DEBUG_H
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
extern DebuggerInfoStruct PSX_DBGInfo;
|
||||
|
||||
bool DBG_Init(void);
|
||||
|
||||
void DBG_Break(void);
|
||||
|
||||
void DBG_GPUScanlineHook(unsigned scanline);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
408
mednafen/psx/dis.cpp
Normal file
408
mednafen/psx/dis.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
struct OpEntry
|
||||
{
|
||||
uint32 mask;
|
||||
uint32 value;
|
||||
const char *mnemonic;
|
||||
const char *format;
|
||||
};
|
||||
|
||||
#define MASK_OP (0x3FU << 26)
|
||||
#define MASK_FUNC (0x3FU)
|
||||
#define MASK_RS (0x1FU << 21)
|
||||
#define MASK_RT (0x1FU << 16)
|
||||
#define MASK_RD (0x1FU << 11)
|
||||
#define MASK_SA (0x1FU << 6)
|
||||
|
||||
#define MK_OP(mnemonic, format, op, func, extra_mask) { MASK_OP | (op ? 0 : MASK_FUNC) | extra_mask, ((unsigned)op << 26) | func, mnemonic, format }
|
||||
|
||||
#define MK_OP_REGIMM(mnemonic, regop) { MASK_OP | MASK_RT, (0x01U << 26) | (regop << 16), mnemonic, "s, p" }
|
||||
|
||||
|
||||
#define MK_COPZ(z) { MASK_OP | (0x1U << 25), (0x1U << 25) | ((0x10U | z) << 26), "cop" #z, "F" }
|
||||
#define MK_COP0_FUNC(mnemonic, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x10U << 26) | (0x1U << 25) | func, mnemonic, "" }
|
||||
|
||||
#define MK_COPZ_XFER(z, mnemonic, format, xf) { MASK_OP | (0x1FU << 21), ((0x10U | z) << 26) | (xf << 21), mnemonic, format }
|
||||
|
||||
#define MK_GTE(mnemonic, format, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x1U << 25) | (0x12U << 26) | func, mnemonic, format }
|
||||
|
||||
static OpEntry ops[] =
|
||||
{
|
||||
MK_OP("nop", "", 0, 0, MASK_RT | MASK_RD | MASK_SA),
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MK_OP("sll", "d, t, a", 0, 0, 0),
|
||||
MK_OP("srl", "d, t, a", 0, 2, 0),
|
||||
MK_OP("sra", "d, t, a", 0, 3, 0),
|
||||
|
||||
MK_OP("sllv", "d, t, s", 0, 4, 0),
|
||||
MK_OP("srlv", "d, t, s", 0, 6, 0),
|
||||
MK_OP("srav", "d, t, s", 0, 7, 0),
|
||||
|
||||
MK_OP("jr", "s", 0, 8, 0),
|
||||
MK_OP("jalr", "d, s", 0, 9, 0),
|
||||
|
||||
MK_OP("syscall", "", 0, 12, 0), // TODO
|
||||
MK_OP("break", "", 0, 13, 0), // TODO
|
||||
|
||||
MK_OP("mfhi", "d", 0, 16, 0),
|
||||
MK_OP("mthi", "s", 0, 17, 0),
|
||||
MK_OP("mflo", "d", 0, 18, 0),
|
||||
MK_OP("mtlo", "s", 0, 19, 0),
|
||||
|
||||
MK_OP("mult", "s, t", 0, 24, 0),
|
||||
MK_OP("multu", "s, t", 0, 25, 0),
|
||||
MK_OP("div", "s, t", 0, 26, 0),
|
||||
MK_OP("divu", "s, t", 0, 27, 0),
|
||||
|
||||
MK_OP("add", "d, s, t", 0, 32, 0),
|
||||
MK_OP("addu", "d, s, t", 0, 33, 0),
|
||||
MK_OP("sub", "d, s, t", 0, 34, 0),
|
||||
MK_OP("subu", "d, s, t", 0, 35, 0),
|
||||
MK_OP("and", "d, s, t", 0, 36, 0),
|
||||
MK_OP("or", "d, s, t", 0, 37, 0),
|
||||
MK_OP("xor", "d, s, t", 0, 38, 0),
|
||||
MK_OP("nor", "d, s, t", 0, 39, 0),
|
||||
MK_OP("slt", "d, s, t", 0, 42, 0),
|
||||
MK_OP("sltu", "d, s, t", 0, 43, 0),
|
||||
|
||||
MK_OP_REGIMM("bgez", 0x01),
|
||||
MK_OP_REGIMM("bgezal", 0x11),
|
||||
MK_OP_REGIMM("bltz", 0x00),
|
||||
MK_OP_REGIMM("bltzal", 0x10),
|
||||
|
||||
|
||||
MK_OP("j", "P", 2, 0, 0),
|
||||
MK_OP("jal", "P", 3, 0, 0),
|
||||
|
||||
MK_OP("beq", "s, t, p", 4, 0, 0),
|
||||
MK_OP("bne", "s, t, p", 5, 0, 0),
|
||||
MK_OP("blez", "s, p", 6, 0, 0),
|
||||
MK_OP("bgtz", "s, p", 7, 0, 0),
|
||||
|
||||
MK_OP("addi", "t, s, i", 8, 0, 0),
|
||||
MK_OP("addiu", "t, s, i", 9, 0, 0),
|
||||
MK_OP("slti", "t, s, i", 10, 0, 0),
|
||||
MK_OP("sltiu", "t, s, i", 11, 0, 0),
|
||||
|
||||
MK_OP("andi", "t, s, z", 12, 0, 0),
|
||||
|
||||
MK_OP("ori", "t, s, z", 13, 0, 0),
|
||||
MK_OP("xori", "t, s, z", 14, 0, 0),
|
||||
MK_OP("lui", "t, z", 15, 0, 0),
|
||||
|
||||
MK_COPZ_XFER(0, "mfc0", "t, 0", 0x00),
|
||||
MK_COPZ_XFER(1, "mfc1", "t, ?", 0x00),
|
||||
MK_COPZ_XFER(2, "mfc2", "t, g", 0x00),
|
||||
MK_COPZ_XFER(3, "mfc3", "t, ?", 0x00),
|
||||
|
||||
MK_COPZ_XFER(0, "mtc0", "t, 0", 0x04),
|
||||
MK_COPZ_XFER(1, "mtc1", "t, ?", 0x04),
|
||||
MK_COPZ_XFER(2, "mtc2", "t, g", 0x04),
|
||||
MK_COPZ_XFER(3, "mtc3", "t, ?", 0x04),
|
||||
|
||||
MK_COPZ_XFER(0, "cfc0", "t, ?", 0x02),
|
||||
MK_COPZ_XFER(1, "cfc1", "t, ?", 0x02),
|
||||
MK_COPZ_XFER(2, "cfc2", "t, G", 0x02),
|
||||
MK_COPZ_XFER(3, "cfc3", "t, ?", 0x02),
|
||||
|
||||
MK_COPZ_XFER(0, "ctc0", "t, ?", 0x06),
|
||||
MK_COPZ_XFER(1, "ctc1", "t, ?", 0x06),
|
||||
MK_COPZ_XFER(2, "ctc2", "t, G", 0x06),
|
||||
MK_COPZ_XFER(3, "ctc3", "t, ?", 0x06),
|
||||
|
||||
// COP0 stuff here
|
||||
MK_COP0_FUNC("rfe", 0x10),
|
||||
|
||||
MK_OP("lwc0", "?, i(s)", 0x30, 0, 0),
|
||||
MK_OP("lwc1", "?, i(s)", 0x31, 0, 0),
|
||||
MK_OP("lwc2", "h, i(s)", 0x32, 0, 0),
|
||||
MK_OP("lwc3", "?, i(s)", 0x33, 0, 0),
|
||||
|
||||
MK_OP("swc0", "?, i(s)", 0x38, 0, 0),
|
||||
MK_OP("swc1", "?, i(s)", 0x39, 0, 0),
|
||||
MK_OP("swc2", "h, i(s)", 0x3A, 0, 0),
|
||||
MK_OP("swc3", "?, i(s)", 0x3B, 0, 0),
|
||||
|
||||
MK_OP("lb", "t, i(s)", 0x20, 0, 0),
|
||||
MK_OP("lh", "t, i(s)", 0x21, 0, 0),
|
||||
MK_OP("lwl", "t, i(s)", 0x22, 0, 0),
|
||||
MK_OP("lw", "t, i(s)", 0x23, 0, 0),
|
||||
MK_OP("lbu", "t, i(s)", 0x24, 0, 0),
|
||||
MK_OP("lhu", "t, i(s)", 0x25, 0, 0),
|
||||
MK_OP("lwr", "t, i(s)", 0x26, 0, 0),
|
||||
|
||||
MK_OP("sb", "t, i(s)", 0x28, 0, 0),
|
||||
MK_OP("sh", "t, i(s)", 0x29, 0, 0),
|
||||
MK_OP("swl", "t, i(s)", 0x2A, 0, 0),
|
||||
MK_OP("sw", "t, i(s)", 0x2B, 0, 0),
|
||||
MK_OP("swr", "t, i(s)", 0x2E, 0, 0),
|
||||
|
||||
//
|
||||
// GTE specific instructions
|
||||
//
|
||||
// sf mx v cv lm
|
||||
//
|
||||
MK_GTE("rtps", "#sf# #lm#", 0x00),
|
||||
MK_GTE("rtps", "#sf# #lm#", 0x01),
|
||||
MK_GTE("nclip", "", 0x06),
|
||||
MK_GTE("op", "#sf# #lm#", 0x0C),
|
||||
|
||||
MK_GTE("dpcs", "#sf# #lm#", 0x10),
|
||||
MK_GTE("intpl", "#sf# #lm#", 0x11),
|
||||
MK_GTE("mvmva", "#sf# #mx# #v# #cv# #lm#", 0x12),
|
||||
MK_GTE("ncds", "#sf# #lm#", 0x13),
|
||||
MK_GTE("cdp", "#sf# #lm#", 0x14),
|
||||
MK_GTE("ncdt", "#sf# #lm#", 0x16),
|
||||
MK_GTE("dcpl", "#sf# #lm#", 0x1A),
|
||||
MK_GTE("nccs", "#sf# #lm#", 0x1B),
|
||||
MK_GTE("cc", "#sf# #lm#", 0x1C),
|
||||
MK_GTE("ncs", "#sf# #lm#", 0x1E),
|
||||
MK_GTE("nct", "#sf# #lm#", 0x20),
|
||||
MK_GTE("sqr", "#sf# #lm#", 0x28),
|
||||
MK_GTE("dcpl", "#sf# #lm#", 0x29),
|
||||
MK_GTE("dpct", "#sf# #lm#", 0x2A),
|
||||
MK_GTE("avsz3", "", 0x2D),
|
||||
MK_GTE("avsz4", "", 0x2E),
|
||||
MK_GTE("rtpt", "#sf# #lm#", 0x30),
|
||||
MK_GTE("gpf", "#sf# #lm#", 0x3D),
|
||||
MK_GTE("gpl", "#sf# #lm#", 0x3E),
|
||||
MK_GTE("ncct", "#sf# #lm#", 0x3F),
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MK_COPZ(0),
|
||||
MK_COPZ(1),
|
||||
MK_COPZ(2),
|
||||
MK_COPZ(3),
|
||||
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
std::string DisassembleMIPS(uint32 PC, uint32 instr)
|
||||
{
|
||||
std::string ret = "UNKNOWN";
|
||||
unsigned int rs = (instr >> 21) & 0x1F;
|
||||
unsigned int rt = (instr >> 16) & 0x1F;
|
||||
unsigned int rd = (instr >> 11) & 0x1F;
|
||||
unsigned int shamt = (instr >> 6) & 0x1F;
|
||||
unsigned int immediate = (int32)(int16)(instr & 0xFFFF);
|
||||
unsigned int immediate_ze = (instr & 0xFFFF);
|
||||
unsigned int jt = instr & ((1 << 26) - 1);
|
||||
|
||||
static const char *gpr_names[32] =
|
||||
{
|
||||
"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
|
||||
};
|
||||
|
||||
static const char *cop0_names[32] =
|
||||
{
|
||||
"CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "CPR8", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID",
|
||||
"ERREG", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31"
|
||||
};
|
||||
|
||||
static const char *gte_cr_names[32] =
|
||||
{
|
||||
"R11R12", "R13R21", "R22R23", "R31R32", "R33", "TRX", "TRY", "TRZ", "L11L12", "L13L21", "L22L23", "L31L32", "L33", "RBK", "GBK", "BBK",
|
||||
"LR1LR2", "LR3LG1", "LG2LG3", "LB1LB2", "LB3", "RFC", "GFC", "BFC", "OFX", "OFY", "H", "DQA", "DQB", "ZSF3", "ZSF4", "FLAG"
|
||||
};
|
||||
|
||||
static const char *gte_dr_names[32] =
|
||||
{
|
||||
"VXY0", "VZ0", "VXY1", "VZ1", "VXY2", "VZ2", "RGB", "OTZ", "IR0", "IR1", "IR2", "IR3", "SXY0", "SXY1", "SXY2", "SXYP",
|
||||
"SZ0", "SZ1", "SZ2", "SZ3", "RGB0", "RGB1", "RGB2", "RES1", "MAC0", "MAC1", "MAC2", "MAC3", "IRGB", "ORGB", "LZCS", "LZCR"
|
||||
};
|
||||
|
||||
OpEntry *op = ops;
|
||||
|
||||
while(op->mnemonic)
|
||||
{
|
||||
if((instr & op->mask) == op->value)
|
||||
{
|
||||
// a = shift amount
|
||||
// s = rs
|
||||
// t = rt
|
||||
// d = rd
|
||||
// i = immediate
|
||||
// z = immediate, zero-extended
|
||||
// p = PC + 4 + immediate
|
||||
// P = ((PC + 4) & 0xF0000000) | (26bitval << 2)
|
||||
//
|
||||
// 0 = rd(cop0 registers)
|
||||
// c = rd(copz data registers)
|
||||
// C = rd(copz control registers)
|
||||
// g = rd(GTE data registers)
|
||||
// G = rd(GTE control registers)
|
||||
// h = rt(GTE data registers)
|
||||
|
||||
char s_a[16];
|
||||
char s_i[16];
|
||||
char s_z[16];
|
||||
char s_p[16];
|
||||
char s_P[16];
|
||||
char s_c[16];
|
||||
char s_C[16];
|
||||
|
||||
snprintf(s_a, sizeof(s_a), "%d", shamt);
|
||||
|
||||
if(immediate < 0)
|
||||
snprintf(s_i, sizeof(s_i), "%d", immediate);
|
||||
else
|
||||
snprintf(s_i, sizeof(s_i), "0x%04x", (uint32)immediate);
|
||||
|
||||
snprintf(s_z, sizeof(s_z), "0x%04x", immediate_ze);
|
||||
|
||||
snprintf(s_p, sizeof(s_p), "0x%08x", PC + 4 + (immediate << 2));
|
||||
|
||||
snprintf(s_P, sizeof(s_P), "0x%08x", ((PC + 4) & 0xF0000000) | (jt << 2));
|
||||
|
||||
snprintf(s_c, sizeof(s_c), "CPR%d", rd);
|
||||
snprintf(s_C, sizeof(s_C), "CCR%d", rd);
|
||||
|
||||
ret = std::string(op->mnemonic);
|
||||
ret.append(10 - ret.size(), ' ');
|
||||
|
||||
for(unsigned int i = 0; i < strlen(op->format); i++)
|
||||
{
|
||||
switch(op->format[i])
|
||||
{
|
||||
case '#':
|
||||
// sf mx v cv lm
|
||||
{
|
||||
char as[16];
|
||||
|
||||
as[0] = 0;
|
||||
if(!strncmp(&op->format[i], "#sf#", 4))
|
||||
{
|
||||
i += 3;
|
||||
snprintf(as, 16, "sf=%d", (int)(bool)(instr & (1 << 19)));
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#mx#", 4))
|
||||
{
|
||||
i += 3;
|
||||
snprintf(as, 16, "mx=%d", (instr >> 17) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#v#", 3))
|
||||
{
|
||||
i += 2;
|
||||
snprintf(as, 16, "v=%d", (instr >> 15) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#cv#", 4))
|
||||
{
|
||||
i += 3;
|
||||
snprintf(as, 16, "cv=%d", (instr >> 13) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#lm#", 4))
|
||||
{
|
||||
i += 3;
|
||||
snprintf(as, 16, "lm=%d", (int)(bool)(instr & (1 << 10)));
|
||||
}
|
||||
ret.append(as);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
char s_F[16];
|
||||
|
||||
snprintf(s_F, 16, "0x%07x", instr & 0x1FFFFFF);
|
||||
ret.append(s_F);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
ret.append(gte_dr_names[rt]);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
ret.append(gte_dr_names[rd]);
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
ret.append(gte_cr_names[rd]);
|
||||
break;
|
||||
|
||||
case '0':
|
||||
ret.append(cop0_names[rd]);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
ret.append(s_c);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
ret.append(s_C);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
ret.append(s_a);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
ret.append(s_i);
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
ret.append(s_z);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
ret.append(s_p);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
ret.append(s_P);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
ret.append(gpr_names[rs]);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
ret.append(gpr_names[rt]);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
ret.append(gpr_names[rd]);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret.append(1, op->format[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
op++;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
11
mednafen/psx/dis.h
Normal file
11
mednafen/psx/dis.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_DIS_H
|
||||
#define __MDFN_PSX_DIS_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
std::string DisassembleMIPS(uint32 PC, uint32 instr);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
821
mednafen/psx/dma.cpp
Normal file
821
mednafen/psx/dma.cpp
Normal file
@ -0,0 +1,821 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "mdec.h"
|
||||
#include "cdc.h"
|
||||
#include "spu.h"
|
||||
|
||||
//#include <map>
|
||||
|
||||
// Notes: DMA tested to abort when
|
||||
|
||||
/* Notes:
|
||||
|
||||
Channel 4(SPU):
|
||||
Write:
|
||||
Doesn't seem to work properly with CHCR=0x01000001
|
||||
Hung when CHCR=0x11000601
|
||||
|
||||
Channel 6:
|
||||
DMA hangs if D28 of CHCR is 0?
|
||||
D1 did not have an apparent effect.
|
||||
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
CH_MDEC_IN = 0,
|
||||
CH_MDEC_OUT = 1,
|
||||
CH_GPU = 2,
|
||||
CH_CDC = 3,
|
||||
CH_SPU = 4,
|
||||
CH_FIVE = 5,
|
||||
CH_OT = 6,
|
||||
};
|
||||
|
||||
|
||||
// RunChannels(128 - whatevercounter);
|
||||
//
|
||||
// GPU next event, std::max<128, wait_time>, or something similar, for handling FIFO.
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
static int32 DMACycleCounter;
|
||||
|
||||
static uint32 DMAControl;
|
||||
static uint32 DMAIntControl;
|
||||
static uint8 DMAIntStatus;
|
||||
static bool IRQOut;
|
||||
|
||||
struct Channel
|
||||
{
|
||||
uint32 BaseAddr;
|
||||
uint32 BlockControl;
|
||||
uint32 ChanControl;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint32 CurAddr;
|
||||
uint16 WordCounter;
|
||||
|
||||
//
|
||||
//
|
||||
int32 ClockCounter;
|
||||
};
|
||||
|
||||
static Channel DMACH[7];
|
||||
static pscpu_timestamp_t lastts;
|
||||
|
||||
|
||||
static const char *PrettyChannelNames[7] = { "MDEC IN", "MDEC OUT", "GPU", "CDC", "SPU", "PIO", "OTC" };
|
||||
|
||||
void DMA_Init(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DMA_Kill(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static INLINE void RecalcIRQOut(void)
|
||||
{
|
||||
bool irqo;
|
||||
|
||||
irqo = (bool)(DMAIntStatus & ((DMAIntControl >> 16) & 0x7F));
|
||||
irqo &= (DMAIntControl >> 23) & 1;
|
||||
|
||||
// I think it's logical OR, not XOR/invert. Still kind of weird, maybe it actually does something more complicated?
|
||||
//irqo ^= (DMAIntControl >> 15) & 1;
|
||||
irqo |= (DMAIntControl >> 15) & 1;
|
||||
|
||||
IRQOut = irqo;
|
||||
IRQ_Assert(IRQ_DMA, irqo);
|
||||
}
|
||||
|
||||
void DMA_ResetTS(void)
|
||||
{
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
void DMA_Power(void)
|
||||
{
|
||||
lastts = 0;
|
||||
|
||||
memset(DMACH, 0, sizeof(DMACH));
|
||||
|
||||
DMACycleCounter = 128;
|
||||
|
||||
DMAControl = 0;
|
||||
DMAIntControl = 0;
|
||||
DMAIntStatus = 0;
|
||||
RecalcIRQOut();
|
||||
}
|
||||
|
||||
void PSX_SetDMASuckSuck(unsigned);
|
||||
|
||||
static INLINE bool ChCan(const unsigned ch, const uint32 CRModeCache)
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
|
||||
case CH_MDEC_IN:
|
||||
return(MDEC_DMACanWrite());
|
||||
|
||||
case CH_MDEC_OUT:
|
||||
return(MDEC_DMACanRead());
|
||||
|
||||
case CH_GPU:
|
||||
if(CRModeCache & 0x1)
|
||||
return(GPU->DMACanWrite());
|
||||
else
|
||||
return(true);
|
||||
|
||||
case CH_CDC:
|
||||
return(true);
|
||||
|
||||
case CH_SPU:
|
||||
return(true);
|
||||
|
||||
case CH_FIVE:
|
||||
return(false);
|
||||
|
||||
case CH_OT:
|
||||
return((bool)(DMACH[ch].ChanControl & (1U << 28)));
|
||||
}
|
||||
}
|
||||
|
||||
static void RecalcHalt(void)
|
||||
{
|
||||
bool Halt = false;
|
||||
unsigned ch = 0;
|
||||
|
||||
for(ch = 0; ch < 7; ch++)
|
||||
{
|
||||
if(DMACH[ch].ChanControl & (1U << 24))
|
||||
{
|
||||
if(!(DMACH[ch].ChanControl & (7U << 8)))
|
||||
{
|
||||
if(DMACH[ch].WordCounter > 0)
|
||||
{
|
||||
Halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(DMACH[ch].ChanControl & 0x100) // DMA doesn't hog the bus when this bit is set, though the DMA takes longer.
|
||||
continue;
|
||||
|
||||
if(ch == 4 || ch == 5) // Not sure if these channels will typically hog the bus or not...investigate.
|
||||
continue;
|
||||
|
||||
if(!(DMACH[ch].ChanControl & (1U << 10))) // Not sure about HOGGERYNESS with linked-list mode, and it likely wouldn't work well either in regards
|
||||
// to GPU commands due to the rather large DMA update granularity.
|
||||
{
|
||||
if((DMACH[ch].WordCounter > 0) || ChCan(ch, DMACH[ch].ChanControl & 0x1))
|
||||
{
|
||||
Halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if((DMACH[0].WordCounter || (DMACH[0].ChanControl & (1 << 24))) && (DMACH[0].ChanControl & 0x200) /*&& MDEC_DMACanWrite()*/)
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[1].WordCounter || (DMACH[1].ChanControl & (1 << 24))) && (DMACH[1].ChanControl & 0x200) && (DMACH[1].WordCounter || MDEC_DMACanRead()))
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[2].WordCounter || (DMACH[2].ChanControl & (1 << 24))) && (DMACH[2].ChanControl & 0x200) && ((DMACH[2].ChanControl & 0x1) && (DMACH[2].WordCounter || GPU->DMACanWrite())))
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[3].WordCounter || (DMACH[3].ChanControl & (1 << 24))) && !(DMACH[3].ChanControl & 0x100))
|
||||
Halt = true;
|
||||
|
||||
if(DMACH[6].WordCounter || (DMACH[6].ChanControl & (1 << 24)))
|
||||
Halt = true;
|
||||
#endif
|
||||
|
||||
//printf("Halt: %d\n", Halt);
|
||||
|
||||
if(!Halt && (DMACH[2].ChanControl & (1U << 24)) && ((DMACH[2].ChanControl & 0x700) == 0x200) && ChCan(2, DMACH[2].ChanControl))
|
||||
{
|
||||
unsigned tmp = DMACH[2].BlockControl & 0xFFFF;
|
||||
|
||||
if(tmp > 0)
|
||||
tmp--;
|
||||
|
||||
if(tmp > 200) // Due to 8-bit limitations in the CPU core.
|
||||
tmp = 200;
|
||||
|
||||
PSX_SetDMASuckSuck(tmp);
|
||||
}
|
||||
else
|
||||
PSX_SetDMASuckSuck(0);
|
||||
|
||||
CPU->SetHalt(Halt);
|
||||
}
|
||||
|
||||
|
||||
static INLINE void ChRW(const unsigned ch, const uint32 CRModeCache, uint32 *V)
|
||||
{
|
||||
unsigned extra_cyc_overhead = 0;
|
||||
|
||||
switch(ch)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
|
||||
case CH_MDEC_IN:
|
||||
if(CRModeCache & 0x1)
|
||||
MDEC_DMAWrite(*V);
|
||||
else
|
||||
*V = 0;
|
||||
break;
|
||||
|
||||
case CH_MDEC_OUT:
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
*V = MDEC_DMARead();
|
||||
break;
|
||||
|
||||
case CH_GPU:
|
||||
if(CRModeCache & 0x1)
|
||||
GPU->WriteDMA(*V);
|
||||
else
|
||||
*V = GPU->ReadDMA();
|
||||
break;
|
||||
|
||||
case CH_CDC:
|
||||
// 0x1f801018 affects CDC DMA timing.
|
||||
#if 0
|
||||
if(CRModeCache & 0x100) // For CDC DMA(at least): When this bit is set, DMA controller doesn't appear to hog the (RAM?) bus.
|
||||
{
|
||||
if(CRModeCache & 0x00400000) // For CDC DMA(at least): When this bit is set, DMA controller appears to get even less bus time(or has a lower priority??)
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 44 * 20 / 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 29 * 20 / 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 23 * 20 / 12; // (23 + 1) = 24. (Though closer to 24.5 or 24.4 on average per tests on a PS1)
|
||||
}
|
||||
#endif
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
extra_cyc_overhead = 8; // FIXME: Test.
|
||||
*V = CDC->DMARead(); // Note: Legend of Mana's opening movie is sensitive to DMA timing, including CDC.
|
||||
}
|
||||
break;
|
||||
|
||||
case CH_SPU:
|
||||
// 0x1f801014 affects SPU DMA timing.
|
||||
// Wild conjecture about 0x1f801014:
|
||||
//
|
||||
// & 0x0000000F
|
||||
// & 0x000001E0 --- Used if (& 0x20000000) == 0?
|
||||
// & 0x00001000 --- Double total bus cycle time if value == 0?
|
||||
// & 0x0f000000 --- (value << 1) 33MHz cycles, bus cycle extension(added to 4?)?
|
||||
// & 0x20000000 ---
|
||||
//
|
||||
//
|
||||
// TODO?: SPU DMA will "complete" much faster if there's a mismatch between the CHCR read/write mode bit and the SPU control register DMA mode.
|
||||
//
|
||||
//
|
||||
// Investigate: SPU DMA doesn't seem to work right if the value written to 0x1F801DAA doesn't have the upper bit set to 1(0x8000) on a PS1.
|
||||
|
||||
extra_cyc_overhead = 47; // Should be closer to 69, average, but actual timing is...complicated.
|
||||
|
||||
if(CRModeCache & 0x1)
|
||||
SPU->WriteDMA(*V);
|
||||
else
|
||||
*V = SPU->ReadDMA();
|
||||
break;
|
||||
|
||||
case CH_FIVE:
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
*V = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CH_OT:
|
||||
if(DMACH[ch].WordCounter == 1)
|
||||
*V = 0xFFFFFF;
|
||||
else
|
||||
*V = (DMACH[ch].CurAddr - 4) & 0x1FFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
// GROSS APPROXIMATION, shoehorning multiple effects together, TODO separate(especially SPU and CDC)
|
||||
DMACH[ch].ClockCounter -= std::max<int>(extra_cyc_overhead, (CRModeCache & 0x100) ? 7 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Remember to handle an end condition on the same iteration of the while(DMACH[ch].ClockCounter > 0) loop that caused it,
|
||||
// otherwise RecalcHalt() might take the CPU out of a halted state before the end-of-DMA is signaled(especially a problem considering our largeish
|
||||
// DMA update timing granularity).
|
||||
//
|
||||
static INLINE void RunChannelI(const unsigned ch, const uint32 CRModeCache, int32 clocks)
|
||||
{
|
||||
//const uint32 dc = (DMAControl >> (ch * 4)) & 0xF;
|
||||
|
||||
DMACH[ch].ClockCounter += clocks;
|
||||
|
||||
while(MDFN_LIKELY(DMACH[ch].ClockCounter > 0))
|
||||
{
|
||||
if(DMACH[ch].WordCounter == 0) // Begin WordCounter reload.
|
||||
{
|
||||
if(!(DMACH[ch].ChanControl & (1 << 24))) // Needed for the forced-DMA-stop kludge(see DMA_Write()).
|
||||
break;
|
||||
|
||||
if(!ChCan(ch, CRModeCache))
|
||||
break;
|
||||
|
||||
DMACH[ch].CurAddr = DMACH[ch].BaseAddr;
|
||||
|
||||
if(CRModeCache & (1U << 10))
|
||||
{
|
||||
uint32 header;
|
||||
|
||||
if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
DMAIntControl |= 0x8000;
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
}
|
||||
|
||||
header = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC);
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr + 4) & 0xFFFFFF;
|
||||
|
||||
DMACH[ch].WordCounter = header >> 24;
|
||||
DMACH[ch].BaseAddr = header & 0xFFFFFF;
|
||||
|
||||
// printf to debug Soul Reaver ;)
|
||||
//if(DMACH[ch].WordCounter > 0x10)
|
||||
// printf("What the lala? 0x%02x @ 0x%08x\n", DMACH[ch].WordCounter, DMACH[ch].CurAddr - 4);
|
||||
|
||||
if(DMACH[ch].WordCounter)
|
||||
DMACH[ch].ClockCounter -= 15;
|
||||
else
|
||||
DMACH[ch].ClockCounter -= 10;
|
||||
|
||||
goto SkipPayloadStuff; // 3 cheers for gluten-free spaghetticode(necessary because the newly-loaded WordCounter might be 0, and we actually
|
||||
// want 0 to mean 0 and not 65536 in this context)!
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].WordCounter = DMACH[ch].BlockControl & 0xFFFF;
|
||||
|
||||
if(CRModeCache & (1U << 9))
|
||||
{
|
||||
if(ch == 2) // Technically should apply to all channels, but since we don't implement CPU read penalties for channels other than 2 yet, it's like this to avoid making DMA longer than what games can handle.
|
||||
DMACH[ch].ClockCounter -= 7;
|
||||
|
||||
DMACH[ch].BlockControl = (DMACH[ch].BlockControl & 0xFFFF) | ((DMACH[ch].BlockControl - (1U << 16)) & 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
} // End WordCounter reload.
|
||||
else if(CRModeCache & 0x100) // BLARGH BLARGH FISHWHALE
|
||||
{
|
||||
//printf("LoadWC: %u(oldWC=%u)\n", DMACH[ch].BlockControl & 0xFFFF, DMACH[ch].WordCounter);
|
||||
//MDFN_DispMessage("SPOOOON\n");
|
||||
DMACH[ch].CurAddr = DMACH[ch].BaseAddr;
|
||||
DMACH[ch].WordCounter = DMACH[ch].BlockControl & 0xFFFF;
|
||||
}
|
||||
|
||||
//
|
||||
// Do the payload read/write
|
||||
//
|
||||
{
|
||||
uint32 vtmp;
|
||||
|
||||
if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
DMAIntControl |= 0x8000;
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
}
|
||||
|
||||
if(CRModeCache & 0x1)
|
||||
vtmp = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC);
|
||||
|
||||
ChRW(ch, CRModeCache, &vtmp);
|
||||
|
||||
if(!(CRModeCache & 0x1))
|
||||
MainRAM.WriteU32(DMACH[ch].CurAddr & 0x1FFFFC, vtmp);
|
||||
}
|
||||
|
||||
if(CRModeCache & 0x2)
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr - 4) & 0xFFFFFF;
|
||||
else
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr + 4) & 0xFFFFFF;
|
||||
|
||||
DMACH[ch].WordCounter--;
|
||||
DMACH[ch].ClockCounter--;
|
||||
|
||||
SkipPayloadStuff: ;
|
||||
|
||||
if(CRModeCache & 0x100) // BLARGH BLARGH WHALEFISH
|
||||
{
|
||||
DMACH[ch].BaseAddr = DMACH[ch].CurAddr;
|
||||
DMACH[ch].BlockControl = (DMACH[ch].BlockControl & 0xFFFF0000) | DMACH[ch].WordCounter;
|
||||
//printf("SaveWC: %u\n", DMACH[ch].WordCounter);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle channel end condition:
|
||||
//
|
||||
if(DMACH[ch].WordCounter == 0)
|
||||
{
|
||||
bool ChannelEndTC = false;
|
||||
|
||||
if(!(DMACH[ch].ChanControl & (1 << 24))) // Needed for the forced-DMA-stop kludge(see DMA_Write()).
|
||||
break;
|
||||
|
||||
switch((CRModeCache >> 9) & 0x3)
|
||||
{
|
||||
case 0x0:
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
DMACH[ch].BaseAddr = DMACH[ch].CurAddr;
|
||||
if((DMACH[ch].BlockControl >> 16) == 0)
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
case 0x3: // Not sure about 0x3.
|
||||
if(DMACH[ch].BaseAddr == 0xFFFFFF)
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(ChannelEndTC)
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
if(DMAIntControl & (1U << (16 + ch)))
|
||||
{
|
||||
DMAIntStatus |= 1U << ch;
|
||||
RecalcIRQOut();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(DMACH[ch].ClockCounter > 0)
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
}
|
||||
|
||||
static INLINE void RunChannel(pscpu_timestamp_t timestamp, int32 clocks, int ch)
|
||||
{
|
||||
// Mask out the bits that the DMA controller will modify during the course of operation.
|
||||
const uint32 CRModeCache = DMACH[ch].ChanControl &~(0x11 << 24);
|
||||
|
||||
switch(ch)
|
||||
{
|
||||
default: abort();
|
||||
|
||||
case 0:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(0, 0x00000201, clocks);
|
||||
else
|
||||
RunChannelI(0, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(1, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(1, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000401))
|
||||
RunChannelI(2, 0x00000401, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(2, 0x00000201, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(2, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(2, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000000))
|
||||
RunChannelI(3, 0x00000000, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000100))
|
||||
RunChannelI(3, 0x00000100, clocks);
|
||||
else
|
||||
RunChannelI(3, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(4, 0x00000201, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(4, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(4, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
RunChannelI(5, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000002))
|
||||
RunChannelI(6, 0x00000002, clocks);
|
||||
else
|
||||
RunChannelI(6, CRModeCache, clocks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE int32 CalcNextEvent(int32 next_event)
|
||||
{
|
||||
if(DMACycleCounter < next_event)
|
||||
next_event = DMACycleCounter;
|
||||
|
||||
return(next_event);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t DMA_Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
// uint32 dc = (DMAControl >> (ch * 4)) & 0xF;
|
||||
int32 clocks = timestamp - lastts;
|
||||
lastts = timestamp;
|
||||
|
||||
GPU->Update(timestamp);
|
||||
MDEC_Run(clocks);
|
||||
|
||||
RunChannel(timestamp, clocks, 0);
|
||||
RunChannel(timestamp, clocks, 1);
|
||||
RunChannel(timestamp, clocks, 2);
|
||||
RunChannel(timestamp, clocks, 3);
|
||||
RunChannel(timestamp, clocks, 4);
|
||||
RunChannel(timestamp, clocks, 6);
|
||||
|
||||
DMACycleCounter -= clocks;
|
||||
while(DMACycleCounter <= 0)
|
||||
DMACycleCounter += 128;
|
||||
|
||||
RecalcHalt();
|
||||
|
||||
return(timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void CheckLinkedList(uint32 addr)
|
||||
{
|
||||
std::map<uint32, bool> zoom;
|
||||
|
||||
do
|
||||
{
|
||||
if(zoom[addr])
|
||||
{
|
||||
printf("Bad linked list: 0x%08x\n", addr);
|
||||
break;
|
||||
}
|
||||
zoom[addr] = 1;
|
||||
|
||||
uint32 header = MainRAM.ReadU32(addr & 0x1FFFFC);
|
||||
|
||||
addr = header & 0xFFFFFF;
|
||||
|
||||
} while(addr != 0xFFFFFF && !(addr & 0x800000));
|
||||
}
|
||||
#endif
|
||||
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
|
||||
//if(ch == 2 || ch == 7)
|
||||
//PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus);
|
||||
|
||||
// FIXME if we ever have "accurate" bus emulation
|
||||
V <<= (A & 3) * 8;
|
||||
|
||||
DMA_Update(timestamp);
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V);
|
||||
DMAControl = V;
|
||||
RecalcHalt();
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
//for(int x = 0; x < 7; x++)
|
||||
//{
|
||||
// if(DMACH[x].WordCounter || (DMACH[x].ChanControl & (1 << 24)))
|
||||
// {
|
||||
// fprintf(stderr, "Write DMAIntControl while channel %d active: 0x%08x\n", x, V);
|
||||
// }
|
||||
//}
|
||||
DMAIntControl = V & 0x00ff803f;
|
||||
DMAIntStatus &= ~(V >> 24);
|
||||
|
||||
//if(DMAIntStatus ^ (DMAIntStatus & (V >> 16)))
|
||||
// fprintf(stderr, "DMAINT Fudge: %02x\n", DMAIntStatus ^ (DMAIntStatus & (V >> 16)));
|
||||
DMAIntStatus &= (V >> 16); // THIS IS ALMOST CERTAINLY WRONG AND A HACK. Remove when CDC emulation is better.
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
|
||||
default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF;
|
||||
break;
|
||||
|
||||
case 0x4: DMACH[ch].BlockControl = V;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8:
|
||||
{
|
||||
uint32 OldCC = DMACH[ch].ChanControl;
|
||||
|
||||
//printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl);
|
||||
//
|
||||
// Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the
|
||||
// case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time.
|
||||
//
|
||||
if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24)))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most.
|
||||
RunChannel(timestamp, 128 * 16, ch);
|
||||
DMACH[ch].WordCounter = 0;
|
||||
|
||||
#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes)
|
||||
DMACH[ch].ClockCounter = (1 << 30);
|
||||
RunChannel(timestamp, 1, ch);
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
#endif
|
||||
PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum());
|
||||
//MDFN_DispMessage("[DMA] Forced stop for channel %d", ch);
|
||||
}
|
||||
|
||||
if(ch == 6)
|
||||
DMACH[ch].ChanControl = (V & 0x51000000) | 0x2;
|
||||
else
|
||||
DMACH[ch].ChanControl = V & 0x71770703;
|
||||
|
||||
if(!(OldCC & (1 << 24)) && (V & (1 << 24)))
|
||||
{
|
||||
//PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum());
|
||||
|
||||
DMACH[ch].WordCounter = 0;
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
|
||||
//
|
||||
// Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately(
|
||||
// or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially
|
||||
// games with similar issues).
|
||||
//
|
||||
// Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;)
|
||||
//
|
||||
// Also, it's needed for RecalcHalt() to work with some semblance of workiness.
|
||||
//
|
||||
RunChannel(timestamp, 64, ch); //std::max<int>(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter);
|
||||
}
|
||||
|
||||
RecalcHalt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
uint32 ret = 0;
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
default: PSX_WARNING("[DMA] Unknown read: %08x", A);
|
||||
break;
|
||||
|
||||
case 0x0: ret = DMAControl;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else switch(A & 0xC)
|
||||
{
|
||||
case 0x0: ret = DMACH[ch].BaseAddr;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMACH[ch].BlockControl;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8: ret = DMACH[ch].ChanControl;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ret >>= (A & 3) * 8;
|
||||
|
||||
//PSX_WARNING("[DMA] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
int DMA_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(DMACycleCounter),
|
||||
SFVAR(DMAControl),
|
||||
SFVAR(DMAIntControl),
|
||||
SFVAR(DMAIntStatus),
|
||||
SFVAR(IRQOut),
|
||||
|
||||
#define SFDMACH(n) SFVARN(DMACH[n].BaseAddr, #n "BaseAddr"), \
|
||||
SFVARN(DMACH[n].BlockControl, #n "BlockControl"), \
|
||||
SFVARN(DMACH[n].ChanControl, #n "ChanControl"), \
|
||||
SFVARN(DMACH[n].CurAddr, #n "CurAddr"), \
|
||||
SFVARN(DMACH[n].WordCounter, #n "WordCounter"), \
|
||||
SFVARN(DMACH[n].ClockCounter, #n "ClockCounter")
|
||||
|
||||
SFDMACH(0),
|
||||
SFDMACH(1),
|
||||
SFDMACH(2),
|
||||
SFDMACH(3),
|
||||
SFDMACH(4),
|
||||
SFDMACH(5),
|
||||
SFDMACH(6),
|
||||
|
||||
#undef SFDMACH
|
||||
|
||||
SFVAR(lastts),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "DMA");
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
}
|
24
mednafen/psx/dma.h
Normal file
24
mednafen/psx/dma.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef __MDFN_PSX_DMA_H
|
||||
#define __MDFN_PSX_DMA_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
bool DMA_GPUWriteActive(void);
|
||||
|
||||
pscpu_timestamp_t DMA_Update(const pscpu_timestamp_t timestamp);
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
void DMA_ResetTS(void);
|
||||
|
||||
void DMA_Power(void);
|
||||
|
||||
void DMA_Init(void);
|
||||
void DMA_Kill(void);
|
||||
|
||||
int DMA_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
946
mednafen/psx/frontio.cpp
Normal file
946
mednafen/psx/frontio.cpp
Normal file
@ -0,0 +1,946 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "frontio.h"
|
||||
|
||||
#include "input/gamepad.h"
|
||||
#include "input/dualanalog.h"
|
||||
#include "input/dualshock.h"
|
||||
#include "input/mouse.h"
|
||||
#include "input/negcon.h"
|
||||
#include "input/guncon.h"
|
||||
#include "input/justifier.h"
|
||||
|
||||
#include "input/memcard.h"
|
||||
|
||||
#include "input/multitap.h"
|
||||
|
||||
#define PSX_FIODBGINFO(format, ...) { /* printf(format " -- timestamp=%d -- PAD temp\n", ## __VA_ARGS__, timestamp); */ }
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice::InputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void InputDevice::Power(void)
|
||||
{
|
||||
}
|
||||
|
||||
void InputDevice::Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice::ResetTS(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice::SetAMCT(bool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice::SetCrosshairsColor(uint32 color)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool InputDevice::RequireNoFrameskip(void)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice::GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock)
|
||||
{
|
||||
return(PSX_EVENT_MAXTS);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice::UpdateInput(const void *data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void InputDevice::SetDTR(bool new_dtr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool InputDevice::GetDSR(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
uint32 InputDevice::GetNVSize(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
void InputDevice::ReadNV(uint8 *buffer, uint32 offset, uint32 count)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice::WriteNV(const uint8 *buffer, uint32 offset, uint32 count)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64 InputDevice::GetNVDirtyCount(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
void InputDevice::ResetNVDirtyCount(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static unsigned EP_to_MP(bool emulate_multitap[2], unsigned ep)
|
||||
{
|
||||
if(!emulate_multitap[0] && emulate_multitap[1])
|
||||
{
|
||||
if(ep == 0 || ep >= 5)
|
||||
return(0);
|
||||
else
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
return(ep >= 4);
|
||||
}
|
||||
|
||||
static INLINE unsigned EP_to_SP(bool emulate_multitap[2], unsigned ep)
|
||||
{
|
||||
if(!emulate_multitap[0] && emulate_multitap[1])
|
||||
{
|
||||
if(ep == 0)
|
||||
return(0);
|
||||
else if(ep < 5)
|
||||
return(ep - 1);
|
||||
else
|
||||
return(ep - 4);
|
||||
}
|
||||
else
|
||||
return(ep & 0x3);
|
||||
}
|
||||
|
||||
void FrontIO::MapDevicesToPorts(void)
|
||||
{
|
||||
if(emulate_multitap[0] && emulate_multitap[1])
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
Ports[i] = DevicesTap[i];
|
||||
MCPorts[i] = DummyDevice;
|
||||
}
|
||||
}
|
||||
else if(!emulate_multitap[0] && emulate_multitap[1])
|
||||
{
|
||||
Ports[0] = Devices[0];
|
||||
MCPorts[0] = emulate_memcards[0] ? DevicesMC[0] : DummyDevice;
|
||||
|
||||
Ports[1] = DevicesTap[1];
|
||||
MCPorts[1] = DummyDevice;
|
||||
}
|
||||
else if(emulate_multitap[0] && !emulate_multitap[1])
|
||||
{
|
||||
Ports[0] = DevicesTap[0];
|
||||
MCPorts[0] = DummyDevice;
|
||||
|
||||
Ports[1] = Devices[4];
|
||||
MCPorts[1] = emulate_memcards[4] ? DevicesMC[4] : DummyDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
Ports[i] = Devices[i];
|
||||
MCPorts[i] = emulate_memcards[i] ? DevicesMC[i] : DummyDevice;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("\n");
|
||||
for(unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
unsigned mp = EP_to_MP(emulate_multitap, i);
|
||||
|
||||
if(emulate_multitap[mp])
|
||||
DevicesTap[mp]->SetSubDevice(EP_to_SP(emulate_multitap, i), Devices[i], emulate_memcards[i] ? DevicesMC[i] : DummyDevice);
|
||||
else
|
||||
DevicesTap[mp]->SetSubDevice(EP_to_SP(emulate_multitap, i), DummyDevice, DummyDevice);
|
||||
|
||||
//printf("%d-> multitap: %d, sub-port: %d\n", i, mp, EP_to_SP(emulate_multitap, i));
|
||||
}
|
||||
}
|
||||
|
||||
FrontIO::FrontIO(bool emulate_memcards_[8], bool emulate_multitap_[2])
|
||||
{
|
||||
memcpy(emulate_memcards, emulate_memcards_, sizeof(emulate_memcards));
|
||||
memcpy(emulate_multitap, emulate_multitap_, sizeof(emulate_multitap));
|
||||
|
||||
DummyDevice = new InputDevice();
|
||||
|
||||
for(unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
DeviceData[i] = NULL;
|
||||
Devices[i] = new InputDevice();
|
||||
DevicesMC[i] = Device_Memcard_Create();
|
||||
chair_colors[i] = 1 << 24;
|
||||
Devices[i]->SetCrosshairsColor(chair_colors[i]);
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
DevicesTap[i] = new InputDevice_Multitap();
|
||||
}
|
||||
|
||||
MapDevicesToPorts();
|
||||
}
|
||||
|
||||
void FrontIO::SetAMCT(bool enabled)
|
||||
{
|
||||
for(unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
Devices[i]->SetAMCT(enabled);
|
||||
}
|
||||
amct_enabled = enabled;
|
||||
}
|
||||
|
||||
void FrontIO::SetCrosshairsColor(unsigned port, uint32 color)
|
||||
{
|
||||
assert(port >= 0 && port < 8);
|
||||
|
||||
chair_colors[port] = color;
|
||||
Devices[port]->SetCrosshairsColor(color);
|
||||
}
|
||||
|
||||
FrontIO::~FrontIO()
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if(Devices[i])
|
||||
{
|
||||
delete Devices[i];
|
||||
Devices[i] = NULL;
|
||||
}
|
||||
if(DevicesMC[i])
|
||||
{
|
||||
delete DevicesMC[i];
|
||||
DevicesMC[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if(DevicesTap[i])
|
||||
{
|
||||
delete DevicesTap[i];
|
||||
DevicesTap[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(DummyDevice)
|
||||
{
|
||||
delete DummyDevice;
|
||||
DummyDevice = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pscpu_timestamp_t FrontIO::CalcNextEventTS(pscpu_timestamp_t timestamp, int32 next_event)
|
||||
{
|
||||
pscpu_timestamp_t ret;
|
||||
|
||||
if(ClockDivider > 0 && ClockDivider < next_event)
|
||||
next_event = ClockDivider;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(dsr_pulse_delay[i] > 0 && next_event > dsr_pulse_delay[i])
|
||||
next_event = dsr_pulse_delay[i];
|
||||
|
||||
ret = timestamp + next_event;
|
||||
|
||||
if(irq10_pulse_ts[0] < ret)
|
||||
ret = irq10_pulse_ts[0];
|
||||
|
||||
if(irq10_pulse_ts[1] < ret)
|
||||
ret = irq10_pulse_ts[1];
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static const uint8 ScaleShift[4] = { 0, 0, 4, 6 };
|
||||
|
||||
void FrontIO::CheckStartStopPending(pscpu_timestamp_t timestamp, bool skip_event_set)
|
||||
{
|
||||
//const bool prior_ReceiveInProgress = ReceiveInProgress;
|
||||
//const bool prior_TransmitInProgress = TransmitInProgress;
|
||||
bool trigger_condition = false;
|
||||
|
||||
trigger_condition = (ReceivePending && (Control & 0x4)) || (TransmitPending && (Control & 0x1));
|
||||
|
||||
if(trigger_condition)
|
||||
{
|
||||
if(ReceivePending)
|
||||
{
|
||||
ReceivePending = false;
|
||||
ReceiveInProgress = true;
|
||||
ReceiveBufferAvail = false;
|
||||
ReceiveBuffer = 0;
|
||||
ReceiveBitCounter = 0;
|
||||
}
|
||||
|
||||
if(TransmitPending)
|
||||
{
|
||||
TransmitPending = false;
|
||||
TransmitInProgress = true;
|
||||
TransmitBitCounter = 0;
|
||||
}
|
||||
|
||||
ClockDivider = std::max<uint32>(0x20, (Baudrate << ScaleShift[Mode & 0x3]) & ~1); // Minimum of 0x20 is an emulation sanity check to prevent severe performance degradation.
|
||||
//printf("CD: 0x%02x\n", ClockDivider);
|
||||
}
|
||||
|
||||
if(!(Control & 0x5))
|
||||
{
|
||||
ReceiveInProgress = false;
|
||||
TransmitInProgress = false;
|
||||
}
|
||||
|
||||
if(!ReceiveInProgress && !TransmitInProgress)
|
||||
ClockDivider = 0;
|
||||
|
||||
if(!(skip_event_set))
|
||||
PSX_SetEventNT(PSX_EVENT_FIO, CalcNextEventTS(timestamp, 0x10000000));
|
||||
}
|
||||
|
||||
// DSR IRQ bit setting appears(from indirect tests on real PS1) to be level-sensitive, not edge-sensitive
|
||||
INLINE void FrontIO::DoDSRIRQ(void)
|
||||
{
|
||||
if(Control & 0x1000)
|
||||
{
|
||||
PSX_FIODBGINFO("[DSR] IRQ");
|
||||
istatus = true;
|
||||
IRQ_Assert(IRQ_SIO, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FrontIO::Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
assert(!(A & 0x1));
|
||||
|
||||
PSX_FIODBGINFO("[FIO] Write: %08x %08x", A, V);
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xF)
|
||||
{
|
||||
case 0x0:
|
||||
TransmitBuffer = V;
|
||||
TransmitPending = true;
|
||||
TransmitInProgress = false;
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
Mode = V & 0x013F;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
if(ClockDivider > 0 && ((V & 0x2000) != (Control & 0x2000)) && ((Control & 0x2) == (V & 0x2)) )
|
||||
fprintf(stderr, "FIO device selection changed during comm %04x->%04x", Control, V);
|
||||
|
||||
//printf("Control: %d, %04x\n", timestamp, V);
|
||||
Control = V & 0x3F2F;
|
||||
|
||||
if(V & 0x10)
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
}
|
||||
|
||||
if(V & 0x40) // Reset
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
|
||||
ClockDivider = 0;
|
||||
ReceivePending = false;
|
||||
TransmitPending = false;
|
||||
|
||||
ReceiveInProgress = false;
|
||||
TransmitInProgress = false;
|
||||
|
||||
ReceiveBufferAvail = false;
|
||||
|
||||
TransmitBuffer = 0;
|
||||
ReceiveBuffer = 0;
|
||||
|
||||
ReceiveBitCounter = 0;
|
||||
TransmitBitCounter = 0;
|
||||
|
||||
Mode = 0;
|
||||
Control = 0;
|
||||
Baudrate = 0;
|
||||
}
|
||||
|
||||
Ports[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
MCPorts[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
Ports[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
MCPorts[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
|
||||
#if 1
|
||||
if(!((Control & 0x2) && !(Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[0] = 0;
|
||||
dsr_pulse_delay[2] = 0;
|
||||
dsr_active_until_ts[0] = -1;
|
||||
dsr_active_until_ts[2] = -1;
|
||||
}
|
||||
|
||||
if(!((Control & 0x2) && (Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[1] = 0;
|
||||
dsr_pulse_delay[3] = 0;
|
||||
dsr_active_until_ts[1] = -1;
|
||||
dsr_active_until_ts[3] = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
// TODO: Uncomment out in the future once our CPU emulation is a bit more accurate with timing, to prevent causing problems with games
|
||||
// that may clear the IRQ in an unsafe pattern that only works because its execution was slow enough to allow DSR to go inactive. (Whether or not
|
||||
// such games even exist though is unknown!)
|
||||
//if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
// DoDSRIRQ();
|
||||
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
Baudrate = V;
|
||||
//printf("%02x\n", V);
|
||||
//MDFN_DispMessage("%02x\n", V);
|
||||
break;
|
||||
}
|
||||
|
||||
CheckStartStopPending(timestamp, false);
|
||||
}
|
||||
|
||||
|
||||
uint32 FrontIO::Read(pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
assert(!(A & 0x1));
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xF)
|
||||
{
|
||||
case 0x0:
|
||||
//printf("FIO Read: 0x%02x\n", ReceiveBuffer);
|
||||
ret = ReceiveBuffer | (ReceiveBuffer << 8) | (ReceiveBuffer << 16) | (ReceiveBuffer << 24);
|
||||
ReceiveBufferAvail = false;
|
||||
ReceivePending = true;
|
||||
ReceiveInProgress = false;
|
||||
CheckStartStopPending(timestamp, false);
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
ret = 0;
|
||||
|
||||
if(!TransmitPending && !TransmitInProgress)
|
||||
ret |= 0x1;
|
||||
|
||||
if(ReceiveBufferAvail)
|
||||
ret |= 0x2;
|
||||
|
||||
if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
ret |= 0x80;
|
||||
|
||||
if(istatus)
|
||||
ret |= 0x200;
|
||||
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
ret = Mode;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
ret = Control;
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
ret = Baudrate;
|
||||
break;
|
||||
}
|
||||
|
||||
if((A & 0xF) != 0x4)
|
||||
PSX_FIODBGINFO("[FIO] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t FrontIO::Update(pscpu_timestamp_t timestamp)
|
||||
{
|
||||
int32 clocks = timestamp - lastts;
|
||||
bool need_start_stop_check = false;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(dsr_pulse_delay[i] > 0)
|
||||
{
|
||||
dsr_pulse_delay[i] -= clocks;
|
||||
if(dsr_pulse_delay[i] <= 0)
|
||||
{
|
||||
dsr_active_until_ts[i] = timestamp + 32 + dsr_pulse_delay[i];
|
||||
DoDSRIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
if(timestamp >= irq10_pulse_ts[i])
|
||||
{
|
||||
//printf("Yay: %d %u\n", i, timestamp);
|
||||
irq10_pulse_ts[i] = PSX_EVENT_MAXTS;
|
||||
IRQ_Assert(IRQ_PIO, true);
|
||||
IRQ_Assert(IRQ_PIO, false);
|
||||
}
|
||||
}
|
||||
|
||||
if(ClockDivider > 0)
|
||||
{
|
||||
ClockDivider -= clocks;
|
||||
|
||||
while(ClockDivider <= 0)
|
||||
{
|
||||
if(ReceiveInProgress || TransmitInProgress)
|
||||
{
|
||||
bool rxd = 0, txd = 0;
|
||||
const uint32 BCMask = 0x07;
|
||||
|
||||
if(TransmitInProgress)
|
||||
{
|
||||
txd = (TransmitBuffer >> TransmitBitCounter) & 1;
|
||||
TransmitBitCounter = (TransmitBitCounter + 1) & BCMask;
|
||||
if(!TransmitBitCounter)
|
||||
{
|
||||
need_start_stop_check = true;
|
||||
PSX_FIODBGINFO("[FIO] Data transmitted: %08x", TransmitBuffer);
|
||||
TransmitInProgress = false;
|
||||
|
||||
if(Control & 0x400)
|
||||
{
|
||||
istatus = true;
|
||||
IRQ_Assert(IRQ_SIO, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rxd = Ports[0]->Clock(txd, dsr_pulse_delay[0]) & Ports[1]->Clock(txd, dsr_pulse_delay[1]) &
|
||||
MCPorts[0]->Clock(txd, dsr_pulse_delay[2]) & MCPorts[1]->Clock(txd, dsr_pulse_delay[3]);
|
||||
|
||||
if(ReceiveInProgress)
|
||||
{
|
||||
ReceiveBuffer &= ~(1 << ReceiveBitCounter);
|
||||
ReceiveBuffer |= rxd << ReceiveBitCounter;
|
||||
|
||||
ReceiveBitCounter = (ReceiveBitCounter + 1) & BCMask;
|
||||
|
||||
if(!ReceiveBitCounter)
|
||||
{
|
||||
need_start_stop_check = true;
|
||||
PSX_FIODBGINFO("[FIO] Data received: %08x", ReceiveBuffer);
|
||||
|
||||
ReceiveInProgress = false;
|
||||
ReceiveBufferAvail = true;
|
||||
|
||||
if(Control & 0x800)
|
||||
{
|
||||
istatus = true;
|
||||
IRQ_Assert(IRQ_SIO, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
ClockDivider += std::max<uint32>(0x20, (Baudrate << ScaleShift[Mode & 0x3]) & ~1); // Minimum of 0x20 is an emulation sanity check to prevent severe performance degradation.
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lastts = timestamp;
|
||||
|
||||
|
||||
if(need_start_stop_check)
|
||||
{
|
||||
CheckStartStopPending(timestamp, true);
|
||||
}
|
||||
|
||||
return(CalcNextEventTS(timestamp, 0x10000000));
|
||||
}
|
||||
|
||||
void FrontIO::ResetTS(void)
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
Devices[i]->Update(lastts); // Maybe eventually call Update() from FrontIO::Update() and remove this(but would hurt speed)?
|
||||
Devices[i]->ResetTS();
|
||||
|
||||
DevicesMC[i]->Update(lastts); // Maybe eventually call Update() from FrontIO::Update() and remove this(but would hurt speed)?
|
||||
DevicesMC[i]->ResetTS();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
DevicesTap[i]->Update(lastts);
|
||||
DevicesTap[i]->ResetTS();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
if(irq10_pulse_ts[i] != PSX_EVENT_MAXTS)
|
||||
irq10_pulse_ts[i] -= lastts;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(dsr_active_until_ts[i] >= 0)
|
||||
{
|
||||
dsr_active_until_ts[i] -= lastts;
|
||||
//printf("SPOOONY: %d %d\n", i, dsr_active_until_ts[i]);
|
||||
}
|
||||
}
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
|
||||
void FrontIO::Power(void)
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
dsr_pulse_delay[i] = 0;
|
||||
dsr_active_until_ts[i] = -1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
irq10_pulse_ts[i] = PSX_EVENT_MAXTS;
|
||||
}
|
||||
|
||||
lastts = 0;
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
ClockDivider = 0;
|
||||
|
||||
ReceivePending = false;
|
||||
TransmitPending = false;
|
||||
|
||||
ReceiveInProgress = false;
|
||||
TransmitInProgress = false;
|
||||
|
||||
ReceiveBufferAvail = false;
|
||||
|
||||
TransmitBuffer = 0;
|
||||
ReceiveBuffer = 0;
|
||||
|
||||
ReceiveBitCounter = 0;
|
||||
TransmitBitCounter = 0;
|
||||
|
||||
Mode = 0;
|
||||
Control = 0;
|
||||
Baudrate = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
Devices[i]->Power();
|
||||
DevicesMC[i]->Power();
|
||||
}
|
||||
|
||||
istatus = false;
|
||||
}
|
||||
|
||||
void FrontIO::UpdateInput(void)
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
Devices[i]->UpdateInput(DeviceData[i]);
|
||||
}
|
||||
|
||||
void FrontIO::SetInput(unsigned int port, const char *type, void *ptr)
|
||||
{
|
||||
delete Devices[port];
|
||||
Devices[port] = NULL;
|
||||
|
||||
if(port < 2)
|
||||
irq10_pulse_ts[port] = PSX_EVENT_MAXTS;
|
||||
|
||||
if(!strcmp(type, "gamepad") || !strcmp(type, "dancepad"))
|
||||
Devices[port] = Device_Gamepad_Create();
|
||||
else if(!strcmp(type, "dualanalog"))
|
||||
Devices[port] = Device_DualAnalog_Create(false);
|
||||
else if(!strcmp(type, "analogjoy"))
|
||||
Devices[port] = Device_DualAnalog_Create(true);
|
||||
else if(!strcmp(type, "dualshock"))
|
||||
{
|
||||
char name[256];
|
||||
trio_snprintf(name, 256, _("DualShock on port %u"), port + 1);
|
||||
Devices[port] = Device_DualShock_Create(std::string(name));
|
||||
}
|
||||
else if(!strcmp(type, "mouse"))
|
||||
Devices[port] = Device_Mouse_Create();
|
||||
else if(!strcmp(type, "negcon"))
|
||||
Devices[port] = Device_neGcon_Create();
|
||||
else if(!strcmp(type, "guncon"))
|
||||
Devices[port] = Device_GunCon_Create();
|
||||
else if(!strcmp(type, "justifier"))
|
||||
Devices[port] = Device_Justifier_Create();
|
||||
else
|
||||
Devices[port] = new InputDevice();
|
||||
|
||||
Devices[port]->SetAMCT(amct_enabled);
|
||||
Devices[port]->SetCrosshairsColor(chair_colors[port]);
|
||||
DeviceData[port] = ptr;
|
||||
|
||||
MapDevicesToPorts();
|
||||
}
|
||||
|
||||
uint64 FrontIO::GetMemcardDirtyCount(unsigned int which)
|
||||
{
|
||||
assert(which < 8);
|
||||
|
||||
return(DevicesMC[which]->GetNVDirtyCount());
|
||||
}
|
||||
|
||||
void FrontIO::LoadMemcard(unsigned int which, const char *path)
|
||||
{
|
||||
assert(which < 8);
|
||||
|
||||
try
|
||||
{
|
||||
if(DevicesMC[which]->GetNVSize())
|
||||
{
|
||||
FileStream mf(path, FileStream::MODE_READ);
|
||||
std::vector<uint8> tmpbuf;
|
||||
|
||||
tmpbuf.resize(DevicesMC[which]->GetNVSize());
|
||||
|
||||
if(mf.size() != (int64)tmpbuf.size())
|
||||
throw(MDFN_Error(0, _("Memory card file \"%s\" is an incorrect size(%d bytes). The correct size is %d bytes."), path, (int)mf.size(), (int)tmpbuf.size()));
|
||||
|
||||
mf.read(&tmpbuf[0], tmpbuf.size());
|
||||
|
||||
DevicesMC[which]->WriteNV(&tmpbuf[0], 0, tmpbuf.size());
|
||||
DevicesMC[which]->ResetNVDirtyCount(); // There's no need to rewrite the file if it's the same data.
|
||||
}
|
||||
}
|
||||
catch(MDFN_Error &e)
|
||||
{
|
||||
if(e.GetErrno() != ENOENT)
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
void FrontIO::SaveMemcard(unsigned int which, const char *path)
|
||||
{
|
||||
assert(which < 8);
|
||||
|
||||
if(DevicesMC[which]->GetNVSize() && DevicesMC[which]->GetNVDirtyCount())
|
||||
{
|
||||
FileStream mf(path, FileStream::MODE_WRITE); // TODO: MODE_WRITE_ATOMIC_OVERWRITE
|
||||
std::vector<uint8> tmpbuf;
|
||||
|
||||
tmpbuf.resize(DevicesMC[which]->GetNVSize());
|
||||
|
||||
DevicesMC[which]->ReadNV(&tmpbuf[0], 0, tmpbuf.size());
|
||||
mf.write(&tmpbuf[0], tmpbuf.size());
|
||||
|
||||
mf.close(); // Call before resetting the NV dirty count!
|
||||
|
||||
DevicesMC[which]->ResetNVDirtyCount();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrontIO::RequireNoFrameskip(void)
|
||||
{
|
||||
for(unsigned i = 0; i < 8; i++)
|
||||
if(Devices[i]->RequireNoFrameskip())
|
||||
return(true);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
void FrontIO::GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock)
|
||||
{
|
||||
Update(timestamp);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
pscpu_timestamp_t plts = Devices[i]->GPULineHook(line_timestamp, vsync, pixels, format, width, pix_clock_offset, pix_clock);
|
||||
|
||||
if(i < 2)
|
||||
{
|
||||
irq10_pulse_ts[i] = plts;
|
||||
|
||||
if(irq10_pulse_ts[i] <= timestamp)
|
||||
{
|
||||
irq10_pulse_ts[i] = PSX_EVENT_MAXTS;
|
||||
IRQ_Assert(IRQ_PIO, true);
|
||||
IRQ_Assert(IRQ_PIO, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PSX_SetEventNT(PSX_EVENT_FIO, CalcNextEventTS(timestamp, 0x10000000));
|
||||
}
|
||||
|
||||
static InputDeviceInfoStruct InputDeviceInfoPSXPort[] =
|
||||
{
|
||||
// None
|
||||
{
|
||||
"none",
|
||||
"none",
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
// Gamepad(SCPH-1080)
|
||||
{
|
||||
"gamepad",
|
||||
"Digital Gamepad",
|
||||
"PlayStation digital gamepad; SCPH-1080.",
|
||||
NULL,
|
||||
sizeof(Device_Gamepad_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Gamepad_IDII,
|
||||
},
|
||||
|
||||
// Dual Shock Gamepad(SCPH-1200)
|
||||
{
|
||||
"dualshock",
|
||||
"DualShock",
|
||||
"DualShock gamepad; SCPH-1200. Emulation in Mednafen includes the analog mode toggle button. Rumble is emulated, but currently only supported on Linux, and MS Windows via the XInput API and XInput-compatible gamepads/joysticks. If you're having trouble getting rumble to work on Linux, see if Mednafen is printing out error messages during startup regarding /dev/input/event*, and resolve the issue(s) as necessary.",
|
||||
NULL,
|
||||
sizeof(Device_DualShock_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_DualShock_IDII,
|
||||
},
|
||||
|
||||
// Dual Analog Gamepad(SCPH-1180), forced to analog mode.
|
||||
{
|
||||
"dualanalog",
|
||||
"Dual Analog",
|
||||
"Dual Analog gamepad; SCPH-1180. It is the predecessor/prototype to the more advanced DualShock. Emulated in Mednafen as forced to analog mode, and without rumble.",
|
||||
NULL,
|
||||
sizeof(Device_DualAnalog_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_DualAnalog_IDII,
|
||||
},
|
||||
|
||||
|
||||
// Analog joystick(SCPH-1110), forced to analog mode - emulated through a tweak to dual analog gamepad emulation.
|
||||
{
|
||||
"analogjoy",
|
||||
"Analog Joystick",
|
||||
"Flight-game-oriented dual-joystick controller; SCPH-1110. Emulated in Mednafen as forced to analog mode.",
|
||||
NULL,
|
||||
sizeof(Device_AnalogJoy_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_AnalogJoy_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"mouse",
|
||||
"Mouse",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(Device_Mouse_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Mouse_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"negcon",
|
||||
"neGcon",
|
||||
"Namco's unconventional twisty racing-game-oriented gamepad; NPC-101.",
|
||||
NULL,
|
||||
sizeof(Device_neGcon_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_neGcon_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"guncon",
|
||||
"GunCon",
|
||||
"Namco's light gun; NPC-103.",
|
||||
NULL,
|
||||
sizeof(Device_GunCon_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_GunCon_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"justifier",
|
||||
"Konami Justifier",
|
||||
"Konami's light gun; SLUH-00017. Rumored to be wrought of the coagulated rage of all who tried to shoot The Dog. If the game you want to play supports the \"GunCon\", you should use that instead. NOTE: Currently does not work properly when on any of ports 1B-1D and 2B-2D.",
|
||||
NULL,
|
||||
sizeof(Device_Justifier_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Justifier_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"dancepad",
|
||||
"Dance Pad",
|
||||
"Dingo Dingo Rodeo!",
|
||||
NULL,
|
||||
sizeof(Device_Dancepad_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Dancepad_IDII,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const InputPortInfoStruct PortInfo[] =
|
||||
{
|
||||
{ "port1", "Virtual Port 1", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port2", "Virtual Port 2", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port3", "Virtual Port 3", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port4", "Virtual Port 4", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port5", "Virtual Port 5", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port6", "Virtual Port 6", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port7", "Virtual Port 7", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port8", "Virtual Port 8", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
};
|
||||
|
||||
InputInfoStruct FIO_InputInfo =
|
||||
{
|
||||
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
|
||||
PortInfo
|
||||
};
|
||||
|
||||
|
||||
}
|
142
mednafen/psx/frontio.h
Normal file
142
mednafen/psx/frontio.h
Normal file
@ -0,0 +1,142 @@
|
||||
#ifndef __MDFN_PSX_FRONTIO_H
|
||||
#define __MDFN_PSX_FRONTIO_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Multitap;
|
||||
|
||||
class InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice();
|
||||
virtual ~InputDevice();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock);
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp); // Partially-implemented, don't rely on for timing any more fine-grained than a video frame for now.
|
||||
virtual void ResetTS(void);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetAMCT(bool enabled);
|
||||
virtual void SetCrosshairsColor(uint32 color);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void); // Currently unused.
|
||||
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 count);
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count);
|
||||
|
||||
//
|
||||
// Dirty count should be incremented on each call to a method this class that causes at least 1 write to occur to the
|
||||
// nonvolatile memory(IE Clock() in the correct command phase, and WriteNV()).
|
||||
//
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
};
|
||||
|
||||
class FrontIO
|
||||
{
|
||||
public:
|
||||
|
||||
FrontIO(bool emulate_memcards_[8], bool emulate_multitap_[2]);
|
||||
~FrontIO();
|
||||
|
||||
void Power(void);
|
||||
void Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 Read(pscpu_timestamp_t timestamp, uint32 A);
|
||||
pscpu_timestamp_t CalcNextEventTS(pscpu_timestamp_t timestamp, int32 next_event);
|
||||
pscpu_timestamp_t Update(pscpu_timestamp_t timestamp);
|
||||
void ResetTS(void);
|
||||
|
||||
bool RequireNoFrameskip(void);
|
||||
void GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock);
|
||||
|
||||
void UpdateInput(void);
|
||||
void SetInput(unsigned int port, const char *type, void *ptr);
|
||||
void SetAMCT(bool enabled);
|
||||
void SetCrosshairsColor(unsigned port, uint32 color);
|
||||
|
||||
uint64 GetMemcardDirtyCount(unsigned int which);
|
||||
void LoadMemcard(unsigned int which, const char *path);
|
||||
void SaveMemcard(unsigned int which, const char *path); //, bool force_save = false);
|
||||
|
||||
private:
|
||||
|
||||
void DoDSRIRQ(void);
|
||||
void CheckStartStopPending(pscpu_timestamp_t timestamp, bool skip_event_set = false);
|
||||
|
||||
void MapDevicesToPorts(void);
|
||||
|
||||
bool emulate_memcards[8];
|
||||
bool emulate_multitap[2];
|
||||
|
||||
InputDevice *Ports[2];
|
||||
InputDevice *MCPorts[2];
|
||||
|
||||
InputDevice *DummyDevice;
|
||||
InputDevice_Multitap *DevicesTap[2];
|
||||
|
||||
InputDevice *Devices[8];
|
||||
void *DeviceData[8];
|
||||
|
||||
InputDevice *DevicesMC[8];
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int32 ClockDivider;
|
||||
|
||||
bool ReceivePending;
|
||||
bool TransmitPending;
|
||||
|
||||
bool ReceiveInProgress;
|
||||
bool TransmitInProgress;
|
||||
|
||||
bool ReceiveBufferAvail;
|
||||
|
||||
uint8 ReceiveBuffer;
|
||||
uint8 TransmitBuffer;
|
||||
|
||||
int32 ReceiveBitCounter;
|
||||
int32 TransmitBitCounter;
|
||||
|
||||
uint16 Mode;
|
||||
uint16 Control;
|
||||
uint16 Baudrate;
|
||||
|
||||
|
||||
bool istatus;
|
||||
//
|
||||
//
|
||||
pscpu_timestamp_t irq10_pulse_ts[2];
|
||||
|
||||
int32 dsr_pulse_delay[4];
|
||||
int32 dsr_active_until_ts[4];
|
||||
int32 lastts;
|
||||
//
|
||||
//
|
||||
bool amct_enabled;
|
||||
uint32 chair_colors[8];
|
||||
};
|
||||
|
||||
extern InputInfoStruct FIO_InputInfo;
|
||||
|
||||
}
|
||||
#endif
|
1458
mednafen/psx/gpu.cpp
Normal file
1458
mednafen/psx/gpu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
309
mednafen/psx/gpu.h
Normal file
309
mednafen/psx/gpu.h
Normal file
@ -0,0 +1,309 @@
|
||||
// WARNING WARNING WARNING: ONLY use CanRead() method of BlitterFIFO, and NOT CanWrite(), since the FIFO is larger than the actual PS1 GPU FIFO to accommodate
|
||||
// our lack of fancy superscalarish command sequencer.
|
||||
|
||||
#ifndef __MDFN_PSX_GPU_H
|
||||
#define __MDFN_PSX_GPU_H
|
||||
|
||||
#include "../cdrom/SimpleFIFO.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class PS_GPU;
|
||||
|
||||
struct CTEntry
|
||||
{
|
||||
uint8 len;
|
||||
uint8 fifo_fb_len;
|
||||
bool ss_cmd;
|
||||
const char *name;
|
||||
void (PS_GPU::*func[8])(const uint32 *cb);
|
||||
};
|
||||
|
||||
struct tri_vertex
|
||||
{
|
||||
int32 x, y;
|
||||
int32 u, v;
|
||||
int32 r, g, b;
|
||||
};
|
||||
|
||||
struct i_group;
|
||||
struct i_deltas;
|
||||
|
||||
struct line_point
|
||||
{
|
||||
int32 x, y;
|
||||
uint8 r, g, b;
|
||||
};
|
||||
|
||||
class PS_GPU
|
||||
{
|
||||
public:
|
||||
|
||||
PS_GPU(bool pal_clock_and_tv) MDFN_COLD;
|
||||
~PS_GPU() MDFN_COLD;
|
||||
|
||||
void Power(void) MDFN_COLD;
|
||||
|
||||
void ResetTS(void);
|
||||
|
||||
void StartFrame(EmulateSpecStruct *espec);
|
||||
|
||||
pscpu_timestamp_t Update(const pscpu_timestamp_t timestamp);
|
||||
|
||||
void Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
|
||||
INLINE bool CalcFIFOReadyBit(void)
|
||||
{
|
||||
if(InCmd & (INCMD_PLINE | INCMD_QUAD))
|
||||
return(false);
|
||||
|
||||
if(BlitterFIFO.CanRead() == 0)
|
||||
return(true);
|
||||
|
||||
if(InCmd & (INCMD_FBREAD | INCMD_FBWRITE))
|
||||
return(false);
|
||||
|
||||
if(BlitterFIFO.CanRead() >= Commands[0][BlitterFIFO.ReadUnit(true) >> 24].fifo_fb_len)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
INLINE bool DMACanWrite(void)
|
||||
{
|
||||
return CalcFIFOReadyBit();
|
||||
}
|
||||
|
||||
void WriteDMA(uint32 V);
|
||||
uint32 ReadDMA(void);
|
||||
|
||||
uint32 Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
inline int32 GetScanlineNum(void)
|
||||
{
|
||||
return(scanline);
|
||||
}
|
||||
|
||||
INLINE uint16 PeekRAM(uint32 A)
|
||||
{
|
||||
return(GPURAM[(A >> 10) & 0x1FF][A & 0x3FF]);
|
||||
}
|
||||
|
||||
INLINE void PokeRAM(uint32 A, uint16 V)
|
||||
{
|
||||
GPURAM[(A >> 10) & 0x1FF][A & 0x3FF] = V;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void ProcessFIFO(void);
|
||||
void WriteCB(uint32 data);
|
||||
uint32 ReadData(void);
|
||||
void SoftReset(void);
|
||||
|
||||
// Y, X
|
||||
uint16 GPURAM[512][1024];
|
||||
|
||||
uint32 DMAControl;
|
||||
|
||||
//
|
||||
// Drawing stuff
|
||||
//
|
||||
//int32 TexPageX; // 0, 64, 128, 192, etc up to 960
|
||||
//int32 TexPageY; // 0 or 256
|
||||
//uint32 abr; // Semi-transparency mode(0~3)
|
||||
//bool dtd; // Dithering enable
|
||||
|
||||
int32 ClipX0;
|
||||
int32 ClipY0;
|
||||
int32 ClipX1;
|
||||
int32 ClipY1;
|
||||
|
||||
int32 OffsX;
|
||||
int32 OffsY;
|
||||
|
||||
bool dtd;
|
||||
bool dfe;
|
||||
|
||||
uint32 MaskSetOR;
|
||||
uint32 MaskEvalAND;
|
||||
|
||||
uint8 tww, twh, twx, twy;
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowXLUT_Pre[16];
|
||||
uint8 TexWindowXLUT[256];
|
||||
uint8 TexWindowXLUT_Post[16];
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowYLUT_Pre[16];
|
||||
uint8 TexWindowYLUT[256];
|
||||
uint8 TexWindowYLUT_Post[16];
|
||||
};
|
||||
void RecalcTexWindowLUT(void);
|
||||
|
||||
int32 TexPageX;
|
||||
int32 TexPageY;
|
||||
|
||||
uint32 SpriteFlip;
|
||||
|
||||
uint32 abr;
|
||||
uint32 TexMode;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 RGB8SAT_Under[256];
|
||||
uint8 RGB8SAT[256];
|
||||
uint8 RGB8SAT_Over[256];
|
||||
};
|
||||
|
||||
uint8 DitherLUT[4][4][512]; // Y, X, 8-bit source value(256 extra for saturation)
|
||||
|
||||
bool LineSkipTest(unsigned y);
|
||||
|
||||
template<int BlendMode, bool MaskEval_TA, bool textured>
|
||||
void PlotPixel(int32 x, int32 y, uint16 pix);
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
uint16 GetTexel(uint32 clut_offset, int32 u, int32 v);
|
||||
|
||||
uint16 ModTexel(uint16 texel, int32 r, int32 g, int32 b, const int32 dither_x, const int32 dither_y);
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode, bool MaskEval_TA>
|
||||
void DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, const int32 bv_x, i_group ig, const i_deltas &idl);
|
||||
|
||||
template<bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void DrawTriangle(tri_vertex *vertices, uint32 clut);
|
||||
|
||||
template<int numvertices, bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void Command_DrawPolygon(const uint32 *cb);
|
||||
|
||||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset);
|
||||
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void Command_DrawSprite(const uint32 *cb);
|
||||
|
||||
template<bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void DrawLine(line_point *vertices);
|
||||
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void Command_DrawLine(const uint32 *cb);
|
||||
|
||||
void Command_ClearCache(const uint32 *cb);
|
||||
|
||||
void Command_FBFill(const uint32 *cb);
|
||||
void Command_FBCopy(const uint32 *cb);
|
||||
void Command_FBWrite(const uint32 *cb);
|
||||
void Command_FBRead(const uint32 *cb);
|
||||
|
||||
void Command_DrawMode(const uint32 *cb);
|
||||
void Command_TexWindow(const uint32 *cb);
|
||||
void Command_Clip0(const uint32 *cb);
|
||||
void Command_Clip1(const uint32 *cb);
|
||||
void Command_DrawingOffset(const uint32 *cb);
|
||||
void Command_MaskSetting(const uint32 *cb);
|
||||
|
||||
static CTEntry Commands[4][256];
|
||||
|
||||
SimpleFIFO<uint32> BlitterFIFO;
|
||||
|
||||
uint32 DataReadBuffer;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// Powers of 2 for faster multiple equality testing(just for multi-testing; InCmd itself will only contain 0, or a power of 2).
|
||||
enum
|
||||
{
|
||||
INCMD_NONE = 0,
|
||||
INCMD_PLINE = (1 << 0),
|
||||
INCMD_QUAD = (1 << 1),
|
||||
INCMD_FBWRITE = (1 << 2),
|
||||
INCMD_FBREAD = (1 << 3)
|
||||
};
|
||||
uint8 InCmd;
|
||||
uint8 InCmd_CC;
|
||||
|
||||
tri_vertex InQuad_F3Vertices[3];
|
||||
uint32 InQuad_clut;
|
||||
|
||||
line_point InPLine_PrevPoint;
|
||||
|
||||
uint32 FBRW_X;
|
||||
uint32 FBRW_Y;
|
||||
uint32 FBRW_W;
|
||||
uint32 FBRW_H;
|
||||
uint32 FBRW_CurY;
|
||||
uint32 FBRW_CurX;
|
||||
|
||||
//
|
||||
// Display Parameters
|
||||
//
|
||||
uint32 DisplayMode;
|
||||
|
||||
bool DisplayOff;
|
||||
uint32 DisplayFB_XStart;
|
||||
uint32 DisplayFB_YStart;
|
||||
|
||||
uint32 HorizStart;
|
||||
uint32 HorizEnd;
|
||||
|
||||
uint32 VertStart;
|
||||
uint32 VertEnd;
|
||||
|
||||
//
|
||||
// Display work vars
|
||||
//
|
||||
uint32 DisplayFB_CurYOffset;
|
||||
uint32 DisplayFB_CurLineYReadout;
|
||||
|
||||
bool InVBlank;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint32 LinesPerField;
|
||||
uint32 scanline;
|
||||
bool field;
|
||||
bool field_atvs;
|
||||
bool PhaseChange;
|
||||
|
||||
uint32 DotClockCounter;
|
||||
|
||||
uint64 GPUClockCounter;
|
||||
uint32 GPUClockRatio;
|
||||
int32 LineClockCounter;
|
||||
int32 LinePhase;
|
||||
|
||||
int32 DrawTimeAvail;
|
||||
|
||||
pscpu_timestamp_t lastts;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
bool sl_zero_reached;
|
||||
//
|
||||
//
|
||||
|
||||
EmulateSpecStruct *espec;
|
||||
MDFN_Surface *surface;
|
||||
MDFN_Rect *DisplayRect;
|
||||
MDFN_Rect *LineWidths;
|
||||
bool skip;
|
||||
bool HardwarePALType;
|
||||
|
||||
uint32 OutputLUT[32768];
|
||||
void ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x);
|
||||
|
||||
template<uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift>
|
||||
void ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) NO_INLINE;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
195
mednafen/psx/gpu_command_table.inc
Normal file
195
mednafen/psx/gpu_command_table.inc
Normal file
@ -0,0 +1,195 @@
|
||||
#define POLY_HELPER_SUB(cv, tm, mam) \
|
||||
&PS_GPU::Command_DrawPolygon<3 + ((cv & 0x8) >> 3), ((cv & 0x10) >> 4), ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? BLENDMODE_MAC : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam >
|
||||
|
||||
#define POLY_HELPER(cv) { 1 + (3 /*+ ((cv & 0x8) >> 3)*/) * ( 1 + ((cv & 0x4) >> 2) + ((cv & 0x10) >> 4) ) - ((cv & 0x10) >> 4), \
|
||||
1, false, "Polygon", { POLY_HELPER_SUB(cv, ((cv & 0x4) ? 0 : 0), 0), POLY_HELPER_SUB(cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
POLY_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 0), POLY_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(cv, ((cv & 0x4) ? 0 : 0), 1), POLY_HELPER_SUB(cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
POLY_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 1), POLY_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
} }
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define SPR_HELPER_SUB(cv, tm, mam) &PS_GPU::Command_DrawSprite<(cv >> 3) & 0x3, ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? BLENDMODE_MAC : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam>
|
||||
|
||||
#define SPR_HELPER(cv) { 2 + ((cv & 0x4) >> 2) + ((cv & 0x18) ? 0 : 1), 2 | ((cv & 0x4) >> 2) | ((cv & 0x18) ? 0 : 1), false, "Sprite", { \
|
||||
SPR_HELPER_SUB(cv, ((cv & 0x4) ? 0 : 0), 0), SPR_HELPER_SUB(cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
SPR_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 0), SPR_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(cv, ((cv & 0x4) ? 0 : 0), 1), SPR_HELPER_SUB(cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
SPR_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 1), SPR_HELPER_SUB(cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
} }
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define LINE_HELPER_SUB(cv, mam) &PS_GPU::Command_DrawLine<((cv & 0x08) >> 3), ((cv & 0x10) >> 4), ((cv & 0x2) >> 1) ? BLENDMODE_MAC : -1, mam>
|
||||
|
||||
#define LINE_HELPER(cv) { 3 + ((cv & 0x10) >> 4), 1, false, "Line", \
|
||||
{ LINE_HELPER_SUB(cv, 0), LINE_HELPER_SUB(cv, 0), LINE_HELPER_SUB(cv, 0), LINE_HELPER_SUB(cv, 0), \
|
||||
LINE_HELPER_SUB(cv, 1), LINE_HELPER_SUB(cv, 1), LINE_HELPER_SUB(cv, 1), LINE_HELPER_SUB(cv, 1), } }
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_name, arg_ptr) { arg_cs, arg_fbcs, arg_ss, arg_name, { arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr } }
|
||||
|
||||
#define NULLCMD() { 1, 1, true, NULL, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }
|
||||
|
||||
|
||||
/* 0x00 */
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, "Clear Cache", &PS_GPU::Command_ClearCache),
|
||||
OTHER_HELPER(3, 3, false, "FB Fill", &PS_GPU::Command_FBFill),
|
||||
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x10 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x20 */
|
||||
POLY_HELPER(0x20),
|
||||
POLY_HELPER(0x21),
|
||||
POLY_HELPER(0x22),
|
||||
POLY_HELPER(0x23),
|
||||
POLY_HELPER(0x24),
|
||||
POLY_HELPER(0x25),
|
||||
POLY_HELPER(0x26),
|
||||
POLY_HELPER(0x27),
|
||||
POLY_HELPER(0x28),
|
||||
POLY_HELPER(0x29),
|
||||
POLY_HELPER(0x2a),
|
||||
POLY_HELPER(0x2b),
|
||||
POLY_HELPER(0x2c),
|
||||
POLY_HELPER(0x2d),
|
||||
POLY_HELPER(0x2e),
|
||||
POLY_HELPER(0x2f),
|
||||
POLY_HELPER(0x30),
|
||||
POLY_HELPER(0x31),
|
||||
POLY_HELPER(0x32),
|
||||
POLY_HELPER(0x33),
|
||||
POLY_HELPER(0x34),
|
||||
POLY_HELPER(0x35),
|
||||
POLY_HELPER(0x36),
|
||||
POLY_HELPER(0x37),
|
||||
POLY_HELPER(0x38),
|
||||
POLY_HELPER(0x39),
|
||||
POLY_HELPER(0x3a),
|
||||
POLY_HELPER(0x3b),
|
||||
POLY_HELPER(0x3c),
|
||||
POLY_HELPER(0x3d),
|
||||
POLY_HELPER(0x3e),
|
||||
POLY_HELPER(0x3f),
|
||||
|
||||
LINE_HELPER(0x40),
|
||||
LINE_HELPER(0x41),
|
||||
LINE_HELPER(0x42),
|
||||
LINE_HELPER(0x43),
|
||||
LINE_HELPER(0x44),
|
||||
LINE_HELPER(0x45),
|
||||
LINE_HELPER(0x46),
|
||||
LINE_HELPER(0x47),
|
||||
LINE_HELPER(0x48),
|
||||
LINE_HELPER(0x49),
|
||||
LINE_HELPER(0x4a),
|
||||
LINE_HELPER(0x4b),
|
||||
LINE_HELPER(0x4c),
|
||||
LINE_HELPER(0x4d),
|
||||
LINE_HELPER(0x4e),
|
||||
LINE_HELPER(0x4f),
|
||||
LINE_HELPER(0x50),
|
||||
LINE_HELPER(0x51),
|
||||
LINE_HELPER(0x52),
|
||||
LINE_HELPER(0x53),
|
||||
LINE_HELPER(0x54),
|
||||
LINE_HELPER(0x55),
|
||||
LINE_HELPER(0x56),
|
||||
LINE_HELPER(0x57),
|
||||
LINE_HELPER(0x58),
|
||||
LINE_HELPER(0x59),
|
||||
LINE_HELPER(0x5a),
|
||||
LINE_HELPER(0x5b),
|
||||
LINE_HELPER(0x5c),
|
||||
LINE_HELPER(0x5d),
|
||||
LINE_HELPER(0x5e),
|
||||
LINE_HELPER(0x5f),
|
||||
|
||||
SPR_HELPER(0x60),
|
||||
SPR_HELPER(0x61),
|
||||
SPR_HELPER(0x62),
|
||||
SPR_HELPER(0x63),
|
||||
SPR_HELPER(0x64),
|
||||
SPR_HELPER(0x65),
|
||||
SPR_HELPER(0x66),
|
||||
SPR_HELPER(0x67),
|
||||
SPR_HELPER(0x68),
|
||||
SPR_HELPER(0x69),
|
||||
SPR_HELPER(0x6a),
|
||||
SPR_HELPER(0x6b),
|
||||
SPR_HELPER(0x6c),
|
||||
SPR_HELPER(0x6d),
|
||||
SPR_HELPER(0x6e),
|
||||
SPR_HELPER(0x6f),
|
||||
SPR_HELPER(0x70),
|
||||
SPR_HELPER(0x71),
|
||||
SPR_HELPER(0x72),
|
||||
SPR_HELPER(0x73),
|
||||
SPR_HELPER(0x74),
|
||||
SPR_HELPER(0x75),
|
||||
SPR_HELPER(0x76),
|
||||
SPR_HELPER(0x77),
|
||||
SPR_HELPER(0x78),
|
||||
SPR_HELPER(0x79),
|
||||
SPR_HELPER(0x7a),
|
||||
SPR_HELPER(0x7b),
|
||||
SPR_HELPER(0x7c),
|
||||
SPR_HELPER(0x7d),
|
||||
SPR_HELPER(0x7e),
|
||||
SPR_HELPER(0x7f),
|
||||
|
||||
/* 0x80 */
|
||||
OTHER_HELPER(4, 2, false, "FB Copy", &PS_GPU::Command_FBCopy),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x90 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xA0 */
|
||||
OTHER_HELPER(3, 2, false, "FB Write", &PS_GPU::Command_FBWrite),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xB0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xC0 */
|
||||
OTHER_HELPER(3, 2, false, "FB Read", &PS_GPU::Command_FBRead),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xD0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xE0 */
|
||||
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, "Draw mode settings", &PS_GPU::Command_DrawMode),
|
||||
OTHER_HELPER(1, 2, false, "Texture window settings", &PS_GPU::Command_TexWindow),
|
||||
OTHER_HELPER(1, 1, true, "Drawing area top left", &PS_GPU::Command_Clip0),
|
||||
OTHER_HELPER(1, 1, true, "Drawing area bottom right", &PS_GPU::Command_Clip1),
|
||||
OTHER_HELPER(1, 1, true, "Drawing offset", &PS_GPU::Command_DrawingOffset),
|
||||
OTHER_HELPER(1, 2, false, "Mask settings", &PS_GPU::Command_MaskSetting),
|
||||
|
||||
NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xF0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
249
mednafen/psx/gpu_line.inc
Normal file
249
mednafen/psx/gpu_line.inc
Normal file
@ -0,0 +1,249 @@
|
||||
struct line_fxp_coord
|
||||
{
|
||||
int64 x, y;
|
||||
int32 r, g, b;
|
||||
};
|
||||
|
||||
struct line_fxp_step
|
||||
{
|
||||
int64 dx_dk, dy_dk;
|
||||
int32 dr_dk, dg_dk, db_dk;
|
||||
};
|
||||
|
||||
enum { Line_XY_FractBits = 32 };
|
||||
enum { Line_RGB_FractBits = 12 };
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void LinePointToFXPCoord(const line_point &point, const line_fxp_step &step, line_fxp_coord &coord)
|
||||
{
|
||||
coord.x = ((int64)point.x << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1));
|
||||
coord.y = ((int64)point.y << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1));
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
coord.r = (point.r << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
coord.g = (point.g << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
coord.b = (point.b << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
}
|
||||
|
||||
// Not sure if this is correct or just roughly corresponds to behavior of real system(need more testing):
|
||||
if(step.dx_dk < 0)
|
||||
coord.x--;
|
||||
|
||||
if(step.dy_dk < 0)
|
||||
coord.y--;
|
||||
|
||||
if(step.dr_dk < 0)
|
||||
coord.r--;
|
||||
|
||||
if(step.dg_dk < 0)
|
||||
coord.g--;
|
||||
|
||||
if(step.db_dk < 0)
|
||||
coord.b--;
|
||||
}
|
||||
|
||||
template<typename T, unsigned bits>
|
||||
static INLINE T LineDivide(T delta, int32 dk)
|
||||
{
|
||||
delta <<= bits;
|
||||
|
||||
if(delta < 0)
|
||||
delta -= dk - 1;
|
||||
if(delta > 0)
|
||||
delta += dk - 1;
|
||||
|
||||
return(delta / dk);
|
||||
}
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void LinePointsToFXPStep(const line_point &point0, const line_point &point1, const int32 dk, line_fxp_step &step)
|
||||
{
|
||||
if(!dk)
|
||||
{
|
||||
step.dx_dk = 0;
|
||||
step.dy_dk = 0;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
step.dr_dk = 0;
|
||||
step.dg_dk = 0;
|
||||
step.db_dk = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
step.dx_dk = LineDivide<int64, Line_XY_FractBits>(point1.x - point0.x, dk);
|
||||
step.dy_dk = LineDivide<int64, Line_XY_FractBits>(point1.y - point0.y, dk);
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
step.dr_dk = LineDivide<int32, Line_RGB_FractBits>(point1.r - point0.r, dk);
|
||||
step.dg_dk = LineDivide<int32, Line_RGB_FractBits>(point1.g - point0.g, dk);
|
||||
step.db_dk = LineDivide<int32, Line_RGB_FractBits>(point1.b - point0.b, dk);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void AddLineStep(line_fxp_coord &point, const line_fxp_step &step, int32 count = 1)
|
||||
{
|
||||
point.x += step.dx_dk * count;
|
||||
point.y += step.dy_dk * count;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
point.r += step.dr_dk * count;
|
||||
point.g += step.dg_dk * count;
|
||||
point.b += step.db_dk * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void PS_GPU::DrawLine(line_point *points)
|
||||
{
|
||||
int32 i_dx;
|
||||
int32 i_dy;
|
||||
int32 k;
|
||||
line_fxp_coord cur_point;
|
||||
line_fxp_step step;
|
||||
|
||||
i_dx = abs(points[1].x - points[0].x);
|
||||
i_dy = abs(points[1].y - points[0].y);
|
||||
k = (i_dx > i_dy) ? i_dx : i_dy;
|
||||
|
||||
if(i_dx >= 1024)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Line too long: i_dx=%d", i_dx);
|
||||
return;
|
||||
}
|
||||
|
||||
if(i_dy >= 512)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Line too long: i_dy=%d", i_dy);
|
||||
return;
|
||||
}
|
||||
|
||||
// May not be correct(do tests for the case of k == i_dy on real thing.
|
||||
if(points[0].x > points[1].x)
|
||||
{
|
||||
line_point tmp = points[1];
|
||||
|
||||
points[1] = points[0];
|
||||
points[0] = tmp;
|
||||
}
|
||||
|
||||
DrawTimeAvail -= k * ((BlendMode >= 0) ? 2 : 1);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
LinePointsToFXPStep<goraud>(points[0], points[1], k, step);
|
||||
LinePointToFXPCoord<goraud>(points[0], step, cur_point);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
for(int32 i = 0; i <= k; i++) // <= is not a typo.
|
||||
{
|
||||
// Sign extension is not necessary here for x and y, due to the maximum values that ClipX1 and ClipY1 can contain.
|
||||
const int32 x = (cur_point.x >> Line_XY_FractBits) & 2047;
|
||||
const int32 y = (cur_point.y >> Line_XY_FractBits) & 2047;
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(!LineSkipTest(y))
|
||||
{
|
||||
uint8 r, g, b;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
r = cur_point.r >> Line_RGB_FractBits;
|
||||
g = cur_point.g >> Line_RGB_FractBits;
|
||||
b = cur_point.b >> Line_RGB_FractBits;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = points[0].r;
|
||||
g = points[0].g;
|
||||
b = points[0].b;
|
||||
}
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
// FIXME: There has to be a faster way than checking for being inside the drawing area for each pixel.
|
||||
if(x >= ClipX0 && x <= ClipX1 && y >= ClipY0 && y <= ClipY1)
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
|
||||
AddLineStep<goraud>(cur_point, step);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void PS_GPU::Command_DrawLine(const uint32 *cb)
|
||||
{
|
||||
const uint8 cc = cb[0] >> 24; // For pline handling later.
|
||||
line_point points[2];
|
||||
|
||||
DrawTimeAvail -= 16; // FIXME, correct time.
|
||||
|
||||
if(polyline && InCmd == INCMD_PLINE)
|
||||
{
|
||||
//printf("PLINE N\n");
|
||||
points[0] = InPLine_PrevPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
points[0].r = (*cb >> 0) & 0xFF;
|
||||
points[0].g = (*cb >> 8) & 0xFF;
|
||||
points[0].b = (*cb >> 16) & 0xFF;
|
||||
cb++;
|
||||
|
||||
points[0].x = sign_x_to_s32(11, ((*cb >> 0) & 0xFFFF)) + OffsX;
|
||||
points[0].y = sign_x_to_s32(11, ((*cb >> 16) & 0xFFFF)) + OffsY;
|
||||
cb++;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
points[1].r = (*cb >> 0) & 0xFF;
|
||||
points[1].g = (*cb >> 8) & 0xFF;
|
||||
points[1].b = (*cb >> 16) & 0xFF;
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
points[1].r = points[0].r;
|
||||
points[1].g = points[0].g;
|
||||
points[1].b = points[0].b;
|
||||
}
|
||||
|
||||
points[1].x = sign_x_to_s32(11, ((*cb >> 0) & 0xFFFF)) + OffsX;
|
||||
points[1].y = sign_x_to_s32(11, ((*cb >> 16) & 0xFFFF)) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(polyline)
|
||||
{
|
||||
InPLine_PrevPoint = points[1];
|
||||
|
||||
if(InCmd != INCMD_PLINE)
|
||||
{
|
||||
InCmd = INCMD_PLINE;
|
||||
InCmd_CC = cc;
|
||||
}
|
||||
}
|
||||
|
||||
DrawLine<goraud, BlendMode, MaskEval_TA>(points);
|
||||
}
|
||||
|
489
mednafen/psx/gpu_polygon.inc
Normal file
489
mednafen/psx/gpu_polygon.inc
Normal file
@ -0,0 +1,489 @@
|
||||
struct i_group
|
||||
{
|
||||
int32 u, v;
|
||||
int32 r, g, b;
|
||||
int32 dummy0[3];
|
||||
};
|
||||
|
||||
struct i_deltas
|
||||
{
|
||||
int32 du_dx, dv_dx;
|
||||
int32 dr_dx, dg_dx, db_dx;
|
||||
int32 dummy0[3];
|
||||
|
||||
int32 du_dy, dv_dy;
|
||||
int32 dr_dy, dg_dy, db_dy;
|
||||
int32 dummy1[3];
|
||||
};
|
||||
|
||||
static INLINE int64 MakePolyXFP(int32 x)
|
||||
{
|
||||
return ((int64)x << 32) + ((1LL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
static INLINE int64 MakePolyXFPStep(int32 dx, int32 dy)
|
||||
{
|
||||
int64 ret;
|
||||
int64 dx_ex = (int64)dx << 32;
|
||||
|
||||
if(dx_ex < 0)
|
||||
dx_ex -= dy - 1;
|
||||
|
||||
if(dx_ex > 0)
|
||||
dx_ex += dy - 1;
|
||||
|
||||
ret = dx_ex / dy;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE int32 GetPolyXFP_Int(int64 xfp)
|
||||
{
|
||||
return(xfp >> 32);
|
||||
}
|
||||
|
||||
//#define CALCIS(x,y) ( A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y) )
|
||||
#define CALCIS(x,y) (((B.x - A.x) * (C.y - B.y)) - ((C.x - B.x) * (B.y - A.y)))
|
||||
static INLINE bool CalcIDeltas(i_deltas &idl, const tri_vertex &A, const tri_vertex &B, const tri_vertex &C)
|
||||
{
|
||||
int64 num = ((int64)COORD_MF_INT(1)) << 32;
|
||||
int64 denom = CALCIS(x, y);
|
||||
int64 one_div;
|
||||
|
||||
if(!denom)
|
||||
return(false);
|
||||
|
||||
//num -= abs(denom) - 1;
|
||||
// num += abs(denom) >> 1;
|
||||
|
||||
one_div = num / denom;
|
||||
|
||||
idl.dr_dx = ((one_div * CALCIS(r, y)) + 0x00000000) >> 32;
|
||||
idl.dr_dy = ((one_div * CALCIS(x, r)) + 0x00000000) >> 32;
|
||||
|
||||
idl.dg_dx = ((one_div * CALCIS(g, y)) + 0x00000000) >> 32;
|
||||
idl.dg_dy = ((one_div * CALCIS(x, g)) + 0x00000000) >> 32;
|
||||
|
||||
idl.db_dx = ((one_div * CALCIS(b, y)) + 0x00000000) >> 32;
|
||||
idl.db_dy = ((one_div * CALCIS(x, b)) + 0x00000000) >> 32;
|
||||
|
||||
idl.du_dx = ((one_div * CALCIS(u, y)) + 0x00000000) >> 32;
|
||||
idl.du_dy = ((one_div * CALCIS(x, u)) + 0x00000000) >> 32;
|
||||
|
||||
idl.dv_dx = ((one_div * CALCIS(v, y)) + 0x00000000) >> 32;
|
||||
idl.dv_dy = ((one_div * CALCIS(x, v)) + 0x00000000) >> 32;
|
||||
|
||||
// printf(" du_dx=%08x, du_dy=%08x\n", idl.du_dx, idl.du_dy);
|
||||
|
||||
return(true);
|
||||
}
|
||||
#undef CALCIS
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DX(i_group &ig, const i_deltas &idl)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dx;
|
||||
ig.v += idl.dv_dx;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dx;
|
||||
ig.g += idl.dg_dx;
|
||||
ig.b += idl.db_dx;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DY(i_group &ig, const i_deltas &idl, int32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dy * count;
|
||||
ig.v += idl.dv_dy * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dy * count;
|
||||
ig.g += idl.dg_dy * count;
|
||||
ig.b += idl.db_dy * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, const int32 bv_x, i_group ig, const i_deltas &idl)
|
||||
{
|
||||
int32 xs = x_start, xb = x_bound;
|
||||
|
||||
if(LineSkipTest(y))
|
||||
return;
|
||||
|
||||
if(xs < xb) // (xs != xb)
|
||||
{
|
||||
if(xs < ClipX0)
|
||||
xs = ClipX0;
|
||||
|
||||
if(xb > (ClipX1 + 1))
|
||||
xb = ClipX1 + 1;
|
||||
|
||||
if(xs < xb)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
|
||||
if(goraud || textured)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
}
|
||||
else if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
DrawTimeAvail -= (((xb + 1) & ~1) - (xs & ~1)) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
ig.u += (xs - bv_x) * idl.du_dx;
|
||||
ig.v += (xs - bv_x) * idl.dv_dx;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += (xs - bv_x) * idl.dr_dx;
|
||||
ig.g += (xs - bv_x) * idl.dg_dx;
|
||||
ig.b += (xs - bv_x) * idl.db_dx;
|
||||
}
|
||||
|
||||
for(int32 x = xs; x < xb; x++)
|
||||
{
|
||||
uint32 r, g, b;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
r = RGB8SAT[COORD_GET_INT(ig.r)];
|
||||
g = RGB8SAT[COORD_GET_INT(ig.g)];
|
||||
b = RGB8SAT[COORD_GET_INT(ig.b)];
|
||||
}
|
||||
else
|
||||
{
|
||||
r = COORD_GET_INT(ig.r);
|
||||
g = COORD_GET_INT(ig.g);
|
||||
b = COORD_GET_INT(ig.b);
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, COORD_GET_INT(ig.u), COORD_GET_INT(ig.v));
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
if(dtd)
|
||||
fbw = ModTexel(fbw, r, g, b, x & 3, y & 3);
|
||||
else
|
||||
fbw = ModTexel(fbw, r, g, b, 3, 2); //x & 3, y & 3);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl);
|
||||
//AddStep<goraud, textured>(perp_coord, perp_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void PS_GPU::DrawTriangle(tri_vertex *vertices, uint32 clut)
|
||||
{
|
||||
i_deltas idl;
|
||||
|
||||
#if 0
|
||||
vertices[0].y = COORD_MF_INT(rand());
|
||||
vertices[1].y = COORD_MF_INT(rand());
|
||||
vertices[2].y = COORD_MF_INT(rand());
|
||||
|
||||
vertices[0].x = COORD_MF_INT(rand());
|
||||
vertices[1].x = COORD_MF_INT(rand());
|
||||
vertices[2].x = COORD_MF_INT(rand());
|
||||
#endif
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[1].y < vertices[0].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[0];
|
||||
vertices[0] = vertices[1];
|
||||
vertices[1] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[0].y == vertices[2].y)
|
||||
return;
|
||||
|
||||
if((vertices[2].y - vertices[0].y) >= 512)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle height too large: %d", (vertices[2].y - vertices[0].y));
|
||||
return;
|
||||
}
|
||||
|
||||
if(abs(vertices[2].x - vertices[0].x) >= 1024 ||
|
||||
abs(vertices[2].x - vertices[1].x) >= 1024 ||
|
||||
abs(vertices[1].x - vertices[0].x) >= 1024)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle width too large: %d %d %d", abs(vertices[2].x - vertices[0].x), abs(vertices[2].x - vertices[1].x), abs(vertices[1].x - vertices[0].x));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CalcIDeltas(idl, vertices[0], vertices[1], vertices[2]))
|
||||
return;
|
||||
|
||||
// [0] should be top vertex, [2] should be bottom vertex, [1] should be off to the side vertex.
|
||||
//
|
||||
//
|
||||
int32 y_start = vertices[0].y;
|
||||
int32 y_middle = vertices[1].y;
|
||||
int32 y_bound = vertices[2].y;
|
||||
|
||||
int64 base_coord;
|
||||
int64 base_step;
|
||||
|
||||
int64 bound_coord_ul;
|
||||
int64 bound_coord_us;
|
||||
|
||||
int64 bound_coord_ll;
|
||||
int64 bound_coord_ls;
|
||||
|
||||
bool right_facing;
|
||||
//bool bottom_up;
|
||||
i_group ig;
|
||||
|
||||
ig.u = COORD_MF_INT(vertices[0].u) + (1 << (COORD_FBS - 1));
|
||||
ig.v = COORD_MF_INT(vertices[0].v) + (1 << (COORD_FBS - 1));
|
||||
ig.r = COORD_MF_INT(vertices[0].r);
|
||||
ig.g = COORD_MF_INT(vertices[0].g);
|
||||
ig.b = COORD_MF_INT(vertices[0].b);
|
||||
|
||||
base_coord = MakePolyXFP(vertices[0].x); //COORD_MF_INT(vertices[0].x) + ((1 << COORD_FBS) - 1);
|
||||
base_step = MakePolyXFPStep((vertices[2].x - vertices[0].x), (vertices[2].y - vertices[0].y)); //ROUND_HELPER(COORD_MF_INT(vertices[2].x - vertices[0].x), (vertices[2].y - vertices[0].y));
|
||||
|
||||
bound_coord_ul = MakePolyXFP(vertices[0].x); // + ((1 << COORD_FBS) - 1);
|
||||
bound_coord_ll = MakePolyXFP(vertices[1].x); // + ((1 << COORD_FBS) - 1);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
if(vertices[1].y == vertices[0].y)
|
||||
{
|
||||
bound_coord_us = 0;
|
||||
right_facing = (bool)(vertices[1].x > vertices[0].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
bound_coord_us = MakePolyXFPStep((vertices[1].x - vertices[0].x), (vertices[1].y - vertices[0].y));
|
||||
right_facing = (bool)(bound_coord_us > base_step);
|
||||
}
|
||||
|
||||
if(vertices[2].y == vertices[1].y)
|
||||
bound_coord_ls = 0;
|
||||
else
|
||||
bound_coord_ls = MakePolyXFPStep((vertices[2].x - vertices[1].x), (vertices[2].y - vertices[1].y));
|
||||
|
||||
if(y_start < ClipY0)
|
||||
{
|
||||
int32 count = ClipY0 - y_start;
|
||||
|
||||
y_start = ClipY0;
|
||||
base_coord += base_step * count;
|
||||
bound_coord_ul += bound_coord_us * count;
|
||||
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl, count);
|
||||
|
||||
if(y_middle < ClipY0)
|
||||
{
|
||||
int32 count_ls = ClipY0 - y_middle;
|
||||
|
||||
y_middle = ClipY0;
|
||||
bound_coord_ll += bound_coord_ls * count_ls;
|
||||
}
|
||||
}
|
||||
|
||||
if(y_bound > (ClipY1 + 1))
|
||||
{
|
||||
y_bound = ClipY1 + 1;
|
||||
|
||||
if(y_middle > y_bound)
|
||||
y_middle = y_bound;
|
||||
}
|
||||
|
||||
if(right_facing)
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ul), vertices[0].x, ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl);
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ll), vertices[0].x, ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ul), GetPolyXFP_Int(base_coord), vertices[0].x, ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl);
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ll), GetPolyXFP_Int(base_coord), vertices[0].x, ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("[GPU] Vertices: %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d)\n\n\n", vertices[0].x, vertices[0].y,
|
||||
vertices[0].r, vertices[0].g, vertices[0].b,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[1].r, vertices[1].g, vertices[1].b,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[2].r, vertices[2].g, vertices[2].b);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<int numvertices, bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void PS_GPU::Command_DrawPolygon(const uint32 *cb)
|
||||
{
|
||||
const unsigned cb0 = cb[0];
|
||||
tri_vertex vertices[3];
|
||||
uint32 clut = 0;
|
||||
unsigned sv = 0;
|
||||
//uint32 tpage = 0;
|
||||
|
||||
// Base timing is approximate, and could be improved.
|
||||
if(numvertices == 4 && InCmd == INCMD_QUAD)
|
||||
DrawTimeAvail -= (28 + 18);
|
||||
else
|
||||
DrawTimeAvail -= (64 + 18);
|
||||
|
||||
if(goraud && textured)
|
||||
DrawTimeAvail -= 150 * 3;
|
||||
else if(goraud)
|
||||
DrawTimeAvail -= 96 * 3;
|
||||
else if(textured)
|
||||
DrawTimeAvail -= 60 * 3;
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
memcpy(&vertices[0], &InQuad_F3Vertices[1], 2 * sizeof(tri_vertex));
|
||||
clut = InQuad_clut;
|
||||
sv = 2;
|
||||
}
|
||||
}
|
||||
//else
|
||||
// memset(vertices, 0, sizeof(vertices));
|
||||
|
||||
for(unsigned v = sv; v < 3; v++)
|
||||
{
|
||||
if(v == 0 || goraud)
|
||||
{
|
||||
uint32 raw_color = (*cb & 0xFFFFFF);
|
||||
|
||||
vertices[v].r = raw_color & 0xFF;
|
||||
vertices[v].g = (raw_color >> 8) & 0xFF;
|
||||
vertices[v].b = (raw_color >> 16) & 0xFF;
|
||||
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[v].r = vertices[0].r;
|
||||
vertices[v].g = vertices[0].g;
|
||||
vertices[v].b = vertices[0].b;
|
||||
}
|
||||
|
||||
vertices[v].x = sign_x_to_s32(11, ((int16)(*cb & 0xFFFF))) + OffsX;
|
||||
vertices[v].y = sign_x_to_s32(11, ((int16)(*cb >> 16))) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
vertices[v].u = (*cb & 0xFF);
|
||||
vertices[v].v = (*cb >> 8) & 0xFF;
|
||||
|
||||
if(v == 0)
|
||||
{
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
}
|
||||
|
||||
cb++;
|
||||
}
|
||||
}
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
InCmd = INCMD_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
InCmd = INCMD_QUAD;
|
||||
InCmd_CC = cb0 >> 24;
|
||||
memcpy(&InQuad_F3Vertices[0], &vertices[0], sizeof(tri_vertex) * 3);
|
||||
InQuad_clut = clut;
|
||||
}
|
||||
}
|
||||
|
||||
DrawTriangle<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(vertices, clut);
|
||||
}
|
||||
|
235
mednafen/psx/gpu_sprite.inc
Normal file
235
mednafen/psx/gpu_sprite.inc
Normal file
@ -0,0 +1,235 @@
|
||||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset)
|
||||
{
|
||||
const int32 r = color & 0xFF;
|
||||
const int32 g = (color >> 8) & 0xFF;
|
||||
const int32 b = (color >> 16) & 0xFF;
|
||||
const uint16 fill_color = 0x8000 | ((r >> 3) << 0) | ((g >> 3) << 5) | ((b >> 3) << 10);
|
||||
|
||||
int32 x_start, x_bound;
|
||||
int32 y_start, y_bound;
|
||||
uint8 u, v;
|
||||
int v_inc = 1, u_inc = 1;
|
||||
|
||||
//printf("[GPU] Sprite: x=%d, y=%d, w=%d, h=%d\n", x_arg, y_arg, w, h);
|
||||
|
||||
x_start = x_arg;
|
||||
x_bound = x_arg + w;
|
||||
|
||||
y_start = y_arg;
|
||||
y_bound = y_arg + h;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
u = u_arg;
|
||||
v = v_arg;
|
||||
|
||||
//if(FlipX || FlipY || (u & 1) || (v & 1) || ((TexMode_TA == 0) && ((u & 3) || (v & 3))))
|
||||
// fprintf(stderr, "Flippy: %d %d 0x%02x 0x%02x\n", FlipX, FlipY, u, v);
|
||||
|
||||
if(FlipX)
|
||||
{
|
||||
u_inc = -1;
|
||||
u |= 1;
|
||||
}
|
||||
// FIXME: Something weird happens when lower bit of u is set and we're not doing horizontal flip, but I'm not sure what it is exactly(needs testing)
|
||||
// It may only happen to the first pixel, so look for that case too during testing.
|
||||
//else
|
||||
// u = (u + 1) & ~1;
|
||||
|
||||
if(FlipY)
|
||||
{
|
||||
v_inc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(x_start < ClipX0)
|
||||
{
|
||||
if(textured)
|
||||
u += (ClipX0 - x_start) * u_inc;
|
||||
|
||||
x_start = ClipX0;
|
||||
}
|
||||
|
||||
if(y_start < ClipY0)
|
||||
{
|
||||
if(textured)
|
||||
v += (ClipY0 - y_start) * v_inc;
|
||||
|
||||
y_start = ClipY0;
|
||||
}
|
||||
|
||||
if(x_bound > (ClipX1 + 1))
|
||||
x_bound = ClipX1 + 1;
|
||||
|
||||
if(y_bound > (ClipY1 + 1))
|
||||
y_bound = ClipY1 + 1;
|
||||
|
||||
if(y_bound > y_start && x_bound > x_start)
|
||||
{
|
||||
//
|
||||
// Note(TODO): From tests on a PS1, even a 0-width sprite takes up time to "draw" proportional to its height.
|
||||
//
|
||||
int32 suck_time = (x_bound - x_start) * (y_bound - y_start);
|
||||
|
||||
// Disabled until we can get it to take into account texture windowing, which can cause large sprites to be drawn entirely from cache(and not suffer from a texturing
|
||||
// penalty); and disabled until we find a game that needs more accurate sprite draw timing. :b
|
||||
#if 0
|
||||
if(textured)
|
||||
{
|
||||
// Empirically-observed approximations of time(66MHz cycles) taken to draw large textured sprites in the various texture depths, when the texture data and CLUT data
|
||||
// was zero(non-zero takes longer to draw, TODO test that more):
|
||||
// 4bpp - area * 2
|
||||
// 8bpp - area * 3
|
||||
// 15/16bpp - area * 5
|
||||
// (other factors come into more importance for smaller sprites)
|
||||
static const int cw[4] = { 64, 32, 32, 32 };
|
||||
static const int ch[4] = { 64, 64, 32, 32 };
|
||||
static const int mm[4] = { 2 - 1, 3 - 1, 5 - 1, 5 - 1 };
|
||||
|
||||
// Parts of the first few(up to texture cache height) horizontal lines can be in cache, so assume they are.
|
||||
suck_time += mm[TexMode_TA] * std::max<int>(0, (x_bound - x_start) - cw[TexMode_TA]) * std::min<int>(ch[TexMode_TA], y_bound - y_start);
|
||||
|
||||
// The rest of the horizontal lines should not possibly have parts in the cache now.
|
||||
suck_time += mm[TexMode_TA] * (x_bound - x_start) * std::max<int>(0, (y_bound - y_start) - ch[TexMode_TA]);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
suck_time += ((((x_bound + 1) & ~1) - (x_start & ~1)) * (y_bound - y_start)) >> 1;
|
||||
}
|
||||
|
||||
DrawTimeAvail -= suck_time;
|
||||
}
|
||||
|
||||
|
||||
//HeightMode && !dfe && ((y & 1) == ((DisplayFB_YStart + !field_atvs) & 1)) && !DisplayOff
|
||||
//printf("%d:%d, %d, %d ---- heightmode=%d displayfb_ystart=%d field_atvs=%d displayoff=%d\n", w, h, scanline, dfe, HeightMode, DisplayFB_YStart, field_atvs, DisplayOff);
|
||||
|
||||
for(int32 y = y_start; y < y_bound; y++)
|
||||
{
|
||||
uint8 u_r;
|
||||
|
||||
if(textured)
|
||||
u_r = u;
|
||||
|
||||
if(!LineSkipTest(y))
|
||||
{
|
||||
for(int32 x = x_start; x < x_bound; x++)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, u_r, v);
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
fbw = ModTexel(fbw, r, g, b, 3, 2);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, fill_color);
|
||||
|
||||
if(textured)
|
||||
u_r += u_inc;
|
||||
}
|
||||
}
|
||||
if(textured)
|
||||
v += v_inc;
|
||||
}
|
||||
}
|
||||
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void PS_GPU::Command_DrawSprite(const uint32 *cb)
|
||||
{
|
||||
int32 x, y;
|
||||
int32 w, h;
|
||||
uint8 u = 0, v = 0;
|
||||
uint32 color = 0;
|
||||
uint32 clut = 0;
|
||||
|
||||
DrawTimeAvail -= 16; // FIXME, correct time.
|
||||
|
||||
color = *cb & 0x00FFFFFF;
|
||||
cb++;
|
||||
|
||||
x = sign_x_to_s32(11, (*cb & 0xFFFF));
|
||||
y = sign_x_to_s32(11, (*cb >> 16));
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
u = *cb & 0xFF;
|
||||
v = (*cb >> 8) & 0xFF;
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
cb++;
|
||||
}
|
||||
|
||||
switch(raw_size)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
w = (*cb & 0x3FF);
|
||||
h = (*cb >> 16) & 0x1FF;
|
||||
cb++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
w = 1;
|
||||
h = 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
w = 8;
|
||||
h = 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
w = 16;
|
||||
h = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("SPRITE: %d %d %d -- %d %d\n", raw_size, x, y, w, h);
|
||||
|
||||
x = sign_x_to_s32(11, x + OffsX);
|
||||
y = sign_x_to_s32(11, y + OffsY);
|
||||
|
||||
switch(SpriteFlip & 0x3000)
|
||||
{
|
||||
case 0x0000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x2000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x3000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
1737
mednafen/psx/gte.cpp
Normal file
1737
mednafen/psx/gte.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
mednafen/psx/gte.h
Normal file
21
mednafen/psx/gte.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef __MDFN_PSX_GTE_H
|
||||
#define __MDFN_PSX_GTE_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
void GTE_Power(void);
|
||||
int GTE_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
int32 GTE_Instruction(uint32 instr);
|
||||
|
||||
void GTE_WriteCR(unsigned int which, uint32 value);
|
||||
void GTE_WriteDR(unsigned int which, uint32 value);
|
||||
|
||||
uint32 GTE_ReadCR(unsigned int which);
|
||||
uint32 GTE_ReadDR(unsigned int which);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
32768
mednafen/psx/gte_divrecip.inc
Normal file
32768
mednafen/psx/gte_divrecip.inc
Normal file
File diff suppressed because it is too large
Load Diff
285
mednafen/psx/input/dualanalog.cpp
Normal file
285
mednafen/psx/input/dualanalog.cpp
Normal file
@ -0,0 +1,285 @@
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "dualanalog.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_DualAnalog : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_DualAnalog(bool joystick_mode_);
|
||||
virtual ~InputDevice_DualAnalog();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool joystick_mode;
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
uint8 axes[2][2];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[8];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_DualAnalog::InputDevice_DualAnalog(bool joystick_mode_) : joystick_mode(joystick_mode_)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_DualAnalog::~InputDevice_DualAnalog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_DualAnalog::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
void InputDevice_DualAnalog::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
|
||||
for(int stick = 0; stick < 2; stick++)
|
||||
{
|
||||
for(int axis = 0; axis < 2; axis++)
|
||||
{
|
||||
int32 tmp;
|
||||
|
||||
tmp = 32768 + MDFN_de32lsb((const uint8 *)data + stick * 16 + axis * 8 + 4) - ((int32)MDFN_de32lsb((const uint8 *)data + stick * 16 + axis * 8 + 8) * 32768 / 32767);
|
||||
tmp >>= 8;
|
||||
|
||||
axes[stick][axis] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%d %d %d %d\n", axes[0][0], axes[0][1], axes[1][0], axes[1][1]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_DualAnalog::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_DualAnalog::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_DualAnalog::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = joystick_mode ? 0x53 : 0x73;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_buffer[3] = axes[0][0];
|
||||
transmit_buffer[4] = axes[0][1];
|
||||
transmit_buffer[5] = axes[1][0];
|
||||
transmit_buffer[6] = axes[1][1];
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%d: %02x\n", 7 - transmit_count, receive_buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_DualAnalog_Create(bool joystick_mode)
|
||||
{
|
||||
return new InputDevice_DualAnalog(joystick_mode);
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_DualAnalog_IDII[24] =
|
||||
{
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, NULL },
|
||||
{ "l3", "Left Stick, Button(L3)", 18, IDIT_BUTTON, NULL },
|
||||
{ "r3", "Right stick, Button(R3)", 23, IDIT_BUTTON, NULL },
|
||||
{ "start", "START", 5, IDIT_BUTTON, NULL },
|
||||
{ "up", "D-Pad UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "D-Pad RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "D-Pad DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "D-Pad LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "L2 (rear left shoulder)", 11, IDIT_BUTTON, NULL },
|
||||
{ "r2", "R2 (rear right shoulder)", 13, IDIT_BUTTON, NULL },
|
||||
{ "l1", "L1 (front left shoulder)", 10, IDIT_BUTTON, NULL },
|
||||
{ "r1", "R1 (front right shoulder)", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (upper)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "circle", "○ (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "cross", "x (lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "square", "□ (left)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
|
||||
{ "rstick_right", "Right Stick RIGHT →", 22, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_left", "Right Stick LEFT ←", 21, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_down", "Right Stick DOWN ↓", 20, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_up", "Right Stick UP ↑", 19, IDIT_BUTTON_ANALOG },
|
||||
|
||||
{ "lstick_right", "Left Stick RIGHT →", 17, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_left", "Left Stick LEFT ←", 16, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_down", "Left Stick DOWN ↓", 15, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_up", "Left Stick UP ↑", 14, IDIT_BUTTON_ANALOG },
|
||||
|
||||
};
|
||||
|
||||
// Not sure if all these buttons are named correctly!
|
||||
InputDeviceInputInfoStruct Device_AnalogJoy_IDII[24] =
|
||||
{
|
||||
{ "select", "SELECT", 8, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 9, IDIT_BUTTON, NULL },
|
||||
{ "up", "Thumbstick UP ↑", 14, IDIT_BUTTON, "down" },
|
||||
{ "right", "Thumbstick RIGHT →", 17, IDIT_BUTTON, "left" },
|
||||
{ "down", "Thumbstick DOWN ↓", 15, IDIT_BUTTON, "up" },
|
||||
{ "left", "Thumbstick LEFT ←", 16, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "Left stick, Trigger", 2, IDIT_BUTTON, NULL },
|
||||
{ "r2", "Left stick, Pinky", 3, IDIT_BUTTON, NULL },
|
||||
{ "l1", "Left stick, L-thumb", 0, IDIT_BUTTON, NULL },
|
||||
{ "r1", "Left stick, R-thumb", 1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "Right stick, Pinky", 13, IDIT_BUTTON, NULL },
|
||||
{ "circle", "Right stick, R-thumb", 11, IDIT_BUTTON, NULL },
|
||||
{ "cross", "Right stick, L-thumb", 10, IDIT_BUTTON, NULL },
|
||||
{ "square", "Right stick, Trigger", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "rstick_right", "Right Stick, RIGHT →", 21, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_left", "Right Stick, LEFT ←", 20, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_down", "Right Stick, BACK ↓", 19, IDIT_BUTTON_ANALOG },
|
||||
{ "rstick_up", "Right Stick, FORE ↑", 18, IDIT_BUTTON_ANALOG },
|
||||
|
||||
{ "lstick_right", "Left Stick, RIGHT →", 7, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_left", "Left Stick, LEFT ←", 6, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_down", "Left Stick, BACK ↓", 5, IDIT_BUTTON_ANALOG },
|
||||
{ "lstick_up", "Left Stick, FORE ↑", 4, IDIT_BUTTON_ANALOG },
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
11
mednafen/psx/input/dualanalog.h
Normal file
11
mednafen/psx/input/dualanalog.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_DUALANALOG_H
|
||||
#define __MDFN_PSX_INPUT_DUALANALOG_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_DualAnalog_Create(bool joystick_mode);
|
||||
extern InputDeviceInputInfoStruct Device_DualAnalog_IDII[24];
|
||||
extern InputDeviceInputInfoStruct Device_AnalogJoy_IDII[24];
|
||||
}
|
||||
#endif
|
1025
mednafen/psx/input/dualshock.cpp
Normal file
1025
mednafen/psx/input/dualshock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
mednafen/psx/input/dualshock.h
Normal file
11
mednafen/psx/input/dualshock.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_DUALSHOCK_H
|
||||
#define __MDFN_PSX_INPUT_DUALSHOCK_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
InputDevice *Device_DualShock_Create(const std::string &name);
|
||||
extern InputDeviceInputInfoStruct Device_DualShock_IDII[26];
|
||||
}
|
||||
#endif
|
240
mednafen/psx/input/gamepad.cpp
Normal file
240
mednafen/psx/input/gamepad.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "gamepad.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Gamepad : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Gamepad();
|
||||
virtual ~InputDevice_Gamepad();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[3];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_Gamepad::InputDevice_Gamepad()
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Gamepad::~InputDevice_Gamepad()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Gamepad::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Gamepad::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Gamepad::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Gamepad::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Gamepad::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x41;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
//printf("PAD COmmand 0x42, sl=%u\n", GPU->GetScanlineNum());
|
||||
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_pos = 0;
|
||||
transmit_count = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Gamepad_Create(void)
|
||||
{
|
||||
return new InputDevice_Gamepad();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Gamepad_IDII[16] =
|
||||
{
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 5, IDIT_BUTTON, NULL },
|
||||
{ "up", "UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "L2 (rear left shoulder)", 11, IDIT_BUTTON, NULL },
|
||||
{ "r2", "R2 (rear right shoulder)", 13, IDIT_BUTTON, NULL },
|
||||
{ "l1", "L1 (front left shoulder)", 10, IDIT_BUTTON, NULL },
|
||||
{ "r1", "R1 (front right shoulder)", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (upper)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "circle", "○ (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "cross", "x (lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "square", "□ (left)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
};
|
||||
|
||||
InputDeviceInputInfoStruct Device_Dancepad_IDII[16] =
|
||||
{
|
||||
{ "select", "SELECT", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "up", "UP ↑", 3, IDIT_BUTTON, NULL },
|
||||
{ "right", "RIGHT →", 6, IDIT_BUTTON, NULL },
|
||||
{ "down", "DOWN ↓", 8, IDIT_BUTTON, NULL },
|
||||
{ "left", "LEFT ←", 5, IDIT_BUTTON, NULL },
|
||||
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (lower left)", 7, IDIT_BUTTON, NULL },
|
||||
{ "circle", "○ (upper right)", 4, IDIT_BUTTON, NULL },
|
||||
{ "cross", "x (upper left)", 2, IDIT_BUTTON, NULL },
|
||||
{ "square", "□ (lower right)", 9, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
}
|
12
mednafen/psx/input/gamepad.h
Normal file
12
mednafen/psx/input/gamepad.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __MDFN_PSX_INPUT_GAMEPAD_H
|
||||
#define __MDFN_PSX_INPUT_GAMEPAD_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Gamepad_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Gamepad_IDII[16];
|
||||
extern InputDeviceInputInfoStruct Device_Dancepad_IDII[16];
|
||||
|
||||
}
|
||||
#endif
|
396
mednafen/psx/input/guncon.cpp
Normal file
396
mednafen/psx/input/guncon.cpp
Normal file
@ -0,0 +1,396 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "guncon.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_GunCon : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_GunCon(void);
|
||||
virtual ~InputDevice_GunCon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual void SetCrosshairsColor(uint32 color);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons;
|
||||
bool trigger_eff;
|
||||
bool trigger_noclear;
|
||||
uint16 hit_x, hit_y;
|
||||
|
||||
int32 nom_x, nom_y;
|
||||
int32 os_shot_counter;
|
||||
bool prev_oss;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[16];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
|
||||
//
|
||||
// Video timing stuff
|
||||
bool prev_vsync;
|
||||
int line_counter;
|
||||
|
||||
//
|
||||
unsigned chair_r, chair_g, chair_b;
|
||||
bool draw_chair;
|
||||
};
|
||||
|
||||
InputDevice_GunCon::InputDevice_GunCon(void) : chair_r(0), chair_g(0), chair_b(0), draw_chair(false)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_GunCon::~InputDevice_GunCon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::SetCrosshairsColor(uint32 color)
|
||||
{
|
||||
chair_r = (color >> 16) & 0xFF;
|
||||
chair_g = (color >> 8) & 0xFF;
|
||||
chair_b = (color >> 0) & 0xFF;
|
||||
|
||||
draw_chair = (color != (1 << 24));
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons = 0;
|
||||
trigger_eff = 0;
|
||||
trigger_noclear = 0;
|
||||
hit_x = 0;
|
||||
hit_y = 0;
|
||||
|
||||
nom_x = 0;
|
||||
nom_y = 0;
|
||||
|
||||
os_shot_counter = 0;
|
||||
prev_oss = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
|
||||
prev_vsync = 0;
|
||||
line_counter = 0;
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = MDFN_de32lsb(&d8[0]);
|
||||
nom_y = MDFN_de32lsb(&d8[4]);
|
||||
|
||||
trigger_noclear = (bool)(d8[8] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
||||
buttons = d8[8] >> 1;
|
||||
|
||||
if(os_shot_counter > 0) // FIXME if UpdateInput() is ever called more than once per video frame(at ~50 or ~60Hz).
|
||||
os_shot_counter--;
|
||||
|
||||
// Eeeeiiiiiight.
|
||||
if((d8[8] & 0x8) && !prev_oss && os_shot_counter == 0)
|
||||
os_shot_counter = 4;
|
||||
prev_oss = d8[8] & 0x8;
|
||||
|
||||
//MDFN_DispMessage("%08x %08x", nom_x, nom_y);
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::RequireNoFrameskip(void)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice_GunCon::GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width,
|
||||
const unsigned pix_clock_offset, const unsigned pix_clock)
|
||||
{
|
||||
if(vsync && !prev_vsync)
|
||||
line_counter = 0;
|
||||
|
||||
if(pixels && pix_clock)
|
||||
{
|
||||
const int avs = 16; // Not 16 for PAL, fixme.
|
||||
int32 gx;
|
||||
int32 gy;
|
||||
|
||||
gx = ((int64)nom_x * width / MDFNGameInfo->nominal_width + 0x8000) >> 16;
|
||||
gy = (nom_y + 0x8000) >> 16;
|
||||
|
||||
for(int32 ix = gx; ix < (gx + (int32)(pix_clock / 762925)); ix++)
|
||||
{
|
||||
if(ix >= 0 && (unsigned)ix < width && line_counter >= (avs + gy) && line_counter < (avs + gy + 8))
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[ix], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
{
|
||||
hit_x = (int64)(ix + pix_clock_offset) * 8000000 / pix_clock; // GunCon has what appears to be an 8.00MHz ceramic resonator in it.
|
||||
hit_y = line_counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(draw_chair)
|
||||
{
|
||||
if(line_counter == (avs + gy))
|
||||
{
|
||||
const int ic = pix_clock / 762925;
|
||||
|
||||
for(int32 x = std::max<int32>(0, gx - ic); x < std::min<int32>(width, gx + ic); x++)
|
||||
{
|
||||
int r, g, b, a;
|
||||
int nr, ng, nb;
|
||||
|
||||
format->DecodeColor(pixels[x], r, g, b, a);
|
||||
|
||||
nr = (r + chair_r * 3) >> 2;
|
||||
ng = (g + chair_g * 3) >> 2;
|
||||
nb = (b + chair_b * 3) >> 2;
|
||||
|
||||
if(abs((r * 76 + g * 150 + b * 29) - (nr * 76 + ng * 150 + nb * 29)) < 16384)
|
||||
{
|
||||
nr >>= 1;
|
||||
ng >>= 1;
|
||||
nb >>= 1;
|
||||
}
|
||||
|
||||
pixels[x] = format->MakeColor(nr, ng, nb, a);
|
||||
}
|
||||
}
|
||||
else if(line_counter >= (avs + gy - 8) && line_counter <= (avs + gy + 8))
|
||||
{
|
||||
int r, g, b, a;
|
||||
int nr, ng, nb;
|
||||
|
||||
format->DecodeColor(pixels[gx], r, g, b, a);
|
||||
|
||||
nr = (r + chair_r * 3) >> 2;
|
||||
ng = (g + chair_g * 3) >> 2;
|
||||
nb = (b + chair_b * 3) >> 2;
|
||||
|
||||
if(abs((r * 76 + g * 150 + b * 29) - (nr * 76 + ng * 150 + nb * 29)) < 16384)
|
||||
{
|
||||
nr >>= 1;
|
||||
ng >>= 1;
|
||||
nb >>= 1;
|
||||
}
|
||||
|
||||
pixels[gx] = format->MakeColor(nr, ng, nb, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line_counter++;
|
||||
|
||||
return(PSX_EVENT_MAXTS);
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x63;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%02x\n", receive_buffer);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//puts("MOO");
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "GunCon unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ ((buttons & 0x01) << 3);
|
||||
transmit_buffer[2] = 0xFF ^ (trigger_eff << 5) ^ ((buttons & 0x02) << 5);
|
||||
|
||||
if(os_shot_counter > 0)
|
||||
{
|
||||
hit_x = 0x01;
|
||||
hit_y = 0x0A;
|
||||
transmit_buffer[2] |= (1 << 5);
|
||||
if(os_shot_counter == 2 || os_shot_counter == 3)
|
||||
{
|
||||
transmit_buffer[2] &= ~(1 << 5);
|
||||
}
|
||||
}
|
||||
|
||||
MDFN_en16lsb(&transmit_buffer[3], hit_x);
|
||||
MDFN_en16lsb(&transmit_buffer[5], hit_y);
|
||||
|
||||
hit_x = 0x01;
|
||||
hit_y = 0x0A;
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
|
||||
trigger_eff = trigger_noclear;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 100; //0x80; //0x40;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_GunCon_Create(void)
|
||||
{
|
||||
return new InputDevice_GunCon();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_GunCon_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "a", "A", 1, IDIT_BUTTON, NULL },
|
||||
{ "b", "B", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL }, // Useful for "Judge Dredd", and probably not much else.
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
11
mednafen/psx/input/guncon.h
Normal file
11
mednafen/psx/input/guncon.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_GUNCON_H
|
||||
#define __MDFN_PSX_INPUT_GUNCON_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_GunCon_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_GunCon_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
393
mednafen/psx/input/justifier.cpp
Normal file
393
mednafen/psx/input/justifier.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "justifier.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Justifier : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Justifier(void);
|
||||
virtual ~InputDevice_Justifier();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual void SetCrosshairsColor(uint32 color);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons;
|
||||
bool trigger_eff;
|
||||
bool trigger_noclear;
|
||||
|
||||
bool need_hit_detect;
|
||||
|
||||
int32 nom_x, nom_y;
|
||||
int32 os_shot_counter;
|
||||
bool prev_oss;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[16];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
|
||||
//
|
||||
// Video timing stuff
|
||||
bool prev_vsync;
|
||||
int line_counter;
|
||||
|
||||
//
|
||||
unsigned chair_r, chair_g, chair_b;
|
||||
bool draw_chair;
|
||||
};
|
||||
|
||||
InputDevice_Justifier::InputDevice_Justifier(void) : chair_r(0), chair_g(0), chair_b(0), draw_chair(false)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Justifier::~InputDevice_Justifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::SetCrosshairsColor(uint32 color)
|
||||
{
|
||||
chair_r = (color >> 16) & 0xFF;
|
||||
chair_g = (color >> 8) & 0xFF;
|
||||
chair_b = (color >> 0) & 0xFF;
|
||||
|
||||
draw_chair = (color != (1 << 24));
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons = 0;
|
||||
trigger_eff = 0;
|
||||
trigger_noclear = 0;
|
||||
|
||||
need_hit_detect = false;
|
||||
|
||||
nom_x = 0;
|
||||
nom_y = 0;
|
||||
|
||||
os_shot_counter = 0;
|
||||
prev_oss = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
|
||||
prev_vsync = 0;
|
||||
line_counter = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = MDFN_de32lsb(&d8[0]);
|
||||
nom_y = MDFN_de32lsb(&d8[4]);
|
||||
|
||||
trigger_noclear = (bool)(d8[8] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
||||
buttons = (d8[8] >> 1) & 0x3;
|
||||
|
||||
if(os_shot_counter > 0) // FIXME if UpdateInput() is ever called more than once per video frame(at ~50 or ~60Hz).
|
||||
os_shot_counter--;
|
||||
|
||||
if((d8[8] & 0x8) && !prev_oss && os_shot_counter == 0)
|
||||
os_shot_counter = 10;
|
||||
prev_oss = d8[8] & 0x8;
|
||||
}
|
||||
|
||||
bool InputDevice_Justifier::RequireNoFrameskip(void)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice_Justifier::GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width,
|
||||
const unsigned pix_clock_offset, const unsigned pix_clock)
|
||||
{
|
||||
pscpu_timestamp_t ret = PSX_EVENT_MAXTS;
|
||||
|
||||
if(vsync && !prev_vsync)
|
||||
line_counter = 0;
|
||||
|
||||
if(pixels && pix_clock)
|
||||
{
|
||||
const int avs = 16; // Not 16 for PAL, fixme.
|
||||
int32 gx;
|
||||
int32 gy;
|
||||
int32 gxa;
|
||||
|
||||
gx = ((int64)nom_x * width / MDFNGameInfo->nominal_width + 0x8000) >> 16;
|
||||
gy = (nom_y + 0x8000) >> 16;
|
||||
gxa = gx; // - (pix_clock / 400000);
|
||||
//if(gxa < 0 && gx >= 0)
|
||||
// gxa = 0;
|
||||
|
||||
if(!os_shot_counter && need_hit_detect && gxa >= 0 && gxa < (int)width && line_counter >= (avs + gy - 1) && line_counter <= (avs + gy + 1))
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[gxa], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
{
|
||||
ret = timestamp + (int64)(gxa + pix_clock_offset) * (44100 * 768) / pix_clock - 177;
|
||||
}
|
||||
}
|
||||
|
||||
if(draw_chair)
|
||||
{
|
||||
if(line_counter == (avs + gy))
|
||||
{
|
||||
const int ic = pix_clock / 762925;
|
||||
|
||||
for(int32 x = std::max<int32>(0, gx - ic); x < std::min<int32>(width, gx + ic); x++)
|
||||
{
|
||||
int r, g, b, a;
|
||||
int nr, ng, nb;
|
||||
|
||||
format->DecodeColor(pixels[x], r, g, b, a);
|
||||
|
||||
nr = (r + chair_r * 3) >> 2;
|
||||
ng = (g + chair_g * 3) >> 2;
|
||||
nb = (b + chair_b * 3) >> 2;
|
||||
|
||||
if(abs((r * 76 + g * 150 + b * 29) - (nr * 76 + ng * 150 + nb * 29)) < 16384)
|
||||
{
|
||||
nr >>= 1;
|
||||
ng >>= 1;
|
||||
nb >>= 1;
|
||||
}
|
||||
|
||||
pixels[x] = format->MakeColor(nr, ng, nb, a);
|
||||
}
|
||||
}
|
||||
else if(line_counter >= (avs + gy - 8) && line_counter <= (avs + gy + 8))
|
||||
{
|
||||
int r, g, b, a;
|
||||
int nr, ng, nb;
|
||||
|
||||
format->DecodeColor(pixels[gx], r, g, b, a);
|
||||
|
||||
nr = (r + chair_r * 3) >> 2;
|
||||
ng = (g + chair_g * 3) >> 2;
|
||||
nb = (b + chair_b * 3) >> 2;
|
||||
|
||||
if(abs((r * 76 + g * 150 + b * 29) - (nr * 76 + ng * 150 + nb * 29)) < 16384)
|
||||
{
|
||||
nr >>= 1;
|
||||
ng >>= 1;
|
||||
nb >>= 1;
|
||||
}
|
||||
|
||||
pixels[gx] = format->MakeColor(nr, ng, nb, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line_counter++;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Justifier::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Justifier::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x31;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%02x\n", receive_buffer);
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
need_hit_detect = receive_buffer & 0x10; // TODO, see if it's (val&0x10) == 0x10, or some other mask value.
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Justifier unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ ((buttons & 2) << 2);
|
||||
transmit_buffer[2] = 0xFF ^ (trigger_eff << 7) ^ ((buttons & 1) << 6);
|
||||
|
||||
if(os_shot_counter > 0)
|
||||
{
|
||||
transmit_buffer[2] |= (1 << 7);
|
||||
if(os_shot_counter == 6 || os_shot_counter == 5)
|
||||
{
|
||||
transmit_buffer[2] &= ~(1 << 7);
|
||||
}
|
||||
}
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 3;
|
||||
|
||||
trigger_eff = trigger_noclear;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 200;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Justifier_Create(void)
|
||||
{
|
||||
return new InputDevice_Justifier();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Justifier_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "o", "O", 1, IDIT_BUTTON, NULL },
|
||||
{ "start", "Start", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
11
mednafen/psx/input/justifier.h
Normal file
11
mednafen/psx/input/justifier.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_JUSTIFIER_H
|
||||
#define __MDFN_PSX_INPUT_JUSTIFIER_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Justifier_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Justifier_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
448
mednafen/psx/input/memcard.cpp
Normal file
448
mednafen/psx/input/memcard.cpp
Normal file
@ -0,0 +1,448 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// I could find no other commands than 'R', 'W', and 'S' (not sure what 'S' is for, however)
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "memcard.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Memcard : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Memcard();
|
||||
virtual ~InputDevice_Memcard();
|
||||
|
||||
virtual void Power(void);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 size);
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 size);
|
||||
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
private:
|
||||
|
||||
bool presence_new;
|
||||
|
||||
uint8 card_data[1 << 17];
|
||||
uint8 rw_buffer[128];
|
||||
uint8 write_xor;
|
||||
|
||||
uint64 dirty_count;
|
||||
|
||||
bool dtr;
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
uint16 addr;
|
||||
uint8 calced_xor;
|
||||
|
||||
uint8 transmit_buffer;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_Memcard::InputDevice_Memcard()
|
||||
{
|
||||
Power();
|
||||
|
||||
dirty_count = 0;
|
||||
|
||||
// Init memcard as formatted.
|
||||
assert(sizeof(card_data) == (1 << 17));
|
||||
memset(card_data, 0x00, sizeof(card_data));
|
||||
|
||||
card_data[0x00] = 0x4D;
|
||||
card_data[0x01] = 0x43;
|
||||
card_data[0x7F] = 0x0E;
|
||||
|
||||
for(unsigned int A = 0x80; A < 0x800; A += 0x80)
|
||||
{
|
||||
card_data[A + 0x00] = 0xA0;
|
||||
card_data[A + 0x08] = 0xFF;
|
||||
card_data[A + 0x09] = 0xFF;
|
||||
card_data[A + 0x7F] = 0xA0;
|
||||
}
|
||||
|
||||
for(unsigned int A = 0x0800; A < 0x1200; A += 0x80)
|
||||
{
|
||||
card_data[A + 0x00] = 0xFF;
|
||||
card_data[A + 0x01] = 0xFF;
|
||||
card_data[A + 0x02] = 0xFF;
|
||||
card_data[A + 0x03] = 0xFF;
|
||||
card_data[A + 0x08] = 0xFF;
|
||||
card_data[A + 0x09] = 0xFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InputDevice_Memcard::~InputDevice_Memcard()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
//buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
transmit_buffer = 0;
|
||||
|
||||
transmit_count = 0;
|
||||
|
||||
addr = 0;
|
||||
|
||||
presence_new = true;
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
if(command_phase > 0)
|
||||
PSX_WARNING("[MCR] Communication aborted???");
|
||||
}
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Memcard::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Memcard::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//if(command_phase > 0 || transmit_count)
|
||||
// printf("[MCRDATA] Received_data=0x%02x, Sent_data=0x%02x\n", receive_buffer, transmit_buffer);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x81)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
//printf("[MCR] Device selected\n");
|
||||
transmit_buffer = presence_new ? 0x08 : 0x00;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
//printf("[MCR] Command received: %c\n", command);
|
||||
if(command == 'R' || command == 'W')
|
||||
{
|
||||
command_phase++;
|
||||
transmit_buffer = 0x5A;
|
||||
transmit_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(command == 'S')
|
||||
{
|
||||
PSX_WARNING("[MCR] Memcard S command unsupported.");
|
||||
}
|
||||
|
||||
command_phase = -1;
|
||||
transmit_buffer = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
transmit_buffer = 0x5D;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
transmit_buffer = 0x00;
|
||||
transmit_count = 1;
|
||||
if(command == 'R')
|
||||
command_phase = 1000;
|
||||
else if(command == 'W')
|
||||
command_phase = 2000;
|
||||
break;
|
||||
|
||||
//
|
||||
// Read
|
||||
//
|
||||
case 1000:
|
||||
addr = receive_buffer << 8;
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1001:
|
||||
addr |= receive_buffer & 0xFF;
|
||||
transmit_buffer = '\\';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1002:
|
||||
//printf("[MCR] READ ADDR=0x%04x\n", addr);
|
||||
if(addr >= (sizeof(card_data) >> 7))
|
||||
addr = 0xFFFF;
|
||||
|
||||
calced_xor = 0;
|
||||
transmit_buffer = ']';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
|
||||
// TODO: enable this code(or something like it) when CPU instruction timing is a bit better.
|
||||
//
|
||||
//dsr_pulse_delay = 32000;
|
||||
//goto SkipDPD;
|
||||
//
|
||||
|
||||
break;
|
||||
|
||||
case 1003:
|
||||
transmit_buffer = addr >> 8;
|
||||
calced_xor ^= transmit_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1004:
|
||||
transmit_buffer = addr & 0xFF;
|
||||
calced_xor ^= transmit_buffer;
|
||||
|
||||
if(addr == 0xFFFF)
|
||||
{
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
transmit_count = 1;
|
||||
command_phase = 1024;
|
||||
}
|
||||
break;
|
||||
|
||||
// Transmit actual 128 bytes data
|
||||
case (1024 + 0) ... (1024 + 128 - 1):
|
||||
transmit_buffer = card_data[(addr << 7) + (command_phase - 1024)];
|
||||
calced_xor ^= transmit_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
// XOR
|
||||
case (1024 + 128):
|
||||
transmit_buffer = calced_xor;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
// End flag
|
||||
case (1024 + 129):
|
||||
transmit_buffer = 'G';
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
break;
|
||||
|
||||
//
|
||||
// Write
|
||||
//
|
||||
case 2000:
|
||||
calced_xor = receive_buffer;
|
||||
addr = receive_buffer << 8;
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 2001:
|
||||
calced_xor ^= receive_buffer;
|
||||
addr |= receive_buffer & 0xFF;
|
||||
//printf("[MCR] WRITE ADDR=0x%04x\n", addr);
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase = 2048;
|
||||
break;
|
||||
|
||||
case (2048 + 0) ... (2048 + 128 - 1):
|
||||
calced_xor ^= receive_buffer;
|
||||
rw_buffer[command_phase - 2048] = receive_buffer;
|
||||
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 128): // XOR
|
||||
write_xor = receive_buffer;
|
||||
transmit_buffer = '\\';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 129):
|
||||
transmit_buffer = ']';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 130): // End flag
|
||||
//MDFN_DispMessage("%02x %02x", calced_xor, write_xor);
|
||||
//printf("[MCR] Write End. Actual_XOR=0x%02x, CW_XOR=0x%02x\n", calced_xor, write_xor);
|
||||
|
||||
if(calced_xor != write_xor)
|
||||
transmit_buffer = 'N';
|
||||
else if(addr >= (sizeof(card_data) >> 7))
|
||||
transmit_buffer = 0xFF;
|
||||
else
|
||||
{
|
||||
transmit_buffer = 'G';
|
||||
presence_new = false;
|
||||
|
||||
// If the current data is different from the data to be written, increment the dirty count.
|
||||
// memcpy()'ing over to card_data is also conditionalized here for a slight optimization.
|
||||
if(memcmp(&card_data[addr << 7], rw_buffer, 128))
|
||||
{
|
||||
memcpy(&card_data[addr << 7], rw_buffer, 128);
|
||||
dirty_count++;
|
||||
}
|
||||
}
|
||||
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
//if(command_phase != -1 || transmit_count)
|
||||
// printf("[MCR] Receive: 0x%02x, Send: 0x%02x -- %d\n", receive_buffer, transmit_buffer, command_phase);
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x100;
|
||||
|
||||
//SkipDPD: ;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint32 InputDevice_Memcard::GetNVSize(void)
|
||||
{
|
||||
return(sizeof(card_data));
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::ReadNV(uint8 *buffer, uint32 offset, uint32 size)
|
||||
{
|
||||
while(size--)
|
||||
{
|
||||
*buffer = card_data[offset & (sizeof(card_data) - 1)];
|
||||
buffer++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::WriteNV(const uint8 *buffer, uint32 offset, uint32 size)
|
||||
{
|
||||
if(size)
|
||||
dirty_count++;
|
||||
|
||||
while(size--)
|
||||
{
|
||||
card_data[offset & (sizeof(card_data) - 1)] = *buffer;
|
||||
buffer++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64 InputDevice_Memcard::GetNVDirtyCount(void)
|
||||
{
|
||||
return(dirty_count);
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::ResetNVDirtyCount(void)
|
||||
{
|
||||
dirty_count = 0;
|
||||
}
|
||||
|
||||
|
||||
InputDevice *Device_Memcard_Create(void)
|
||||
{
|
||||
return new InputDevice_Memcard();
|
||||
}
|
||||
|
||||
}
|
11
mednafen/psx/input/memcard.h
Normal file
11
mednafen/psx/input/memcard.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_MEMCARD_H
|
||||
#define __MDFN_PSX_INPUT_MEMCARD_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Memcard_Create(void);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
252
mednafen/psx/input/mouse.cpp
Normal file
252
mednafen/psx/input/mouse.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "mouse.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Mouse : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Mouse();
|
||||
virtual ~InputDevice_Mouse();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp);
|
||||
virtual void ResetTS(void);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
int32 lastts;
|
||||
int32 clear_timeout;
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 button;
|
||||
uint8 button_post_mask;
|
||||
int32 accum_xdelta;
|
||||
int32 accum_ydelta;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[5];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_Mouse::InputDevice_Mouse()
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Mouse::~InputDevice_Mouse()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
int32 cycles = timestamp - lastts;
|
||||
|
||||
clear_timeout += cycles;
|
||||
if(clear_timeout >= (33868800 / 4))
|
||||
{
|
||||
//puts("Mouse timeout\n");
|
||||
clear_timeout = 0;
|
||||
accum_xdelta = 0;
|
||||
accum_ydelta = 0;
|
||||
button &= button_post_mask;
|
||||
}
|
||||
|
||||
lastts = timestamp;
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::ResetTS(void)
|
||||
{
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::Power(void)
|
||||
{
|
||||
lastts = 0;
|
||||
clear_timeout = 0;
|
||||
|
||||
dtr = 0;
|
||||
|
||||
button = 0;
|
||||
button_post_mask = 0;
|
||||
accum_xdelta = 0;
|
||||
accum_ydelta = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::UpdateInput(const void *data)
|
||||
{
|
||||
accum_xdelta += (int32)MDFN_de32lsb((uint8*)data + 0);
|
||||
accum_ydelta += (int32)MDFN_de32lsb((uint8*)data + 4);
|
||||
|
||||
if(accum_xdelta > 30 * 127) accum_xdelta = 30 * 127;
|
||||
if(accum_xdelta < 30 * -128) accum_xdelta = 30 * -128;
|
||||
|
||||
if(accum_ydelta > 30 * 127) accum_ydelta = 30 * 127;
|
||||
if(accum_ydelta < 30 * -128) accum_ydelta = 30 * -128;
|
||||
|
||||
button |= *((uint8 *)data + 8);
|
||||
button_post_mask = *((uint8 *)data + 8);
|
||||
|
||||
//if(button)
|
||||
// MDFN_DispMessage("Button\n");
|
||||
//printf("%d %d\n", accum_xdelta, accum_ydelta);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Mouse::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Mouse::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x12;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
int32 xdelta = accum_xdelta;
|
||||
int32 ydelta = accum_ydelta;
|
||||
|
||||
if(xdelta < -128) xdelta = -128;
|
||||
if(xdelta > 127) xdelta = 127;
|
||||
|
||||
if(ydelta < -128) ydelta = -128;
|
||||
if(ydelta > 127) ydelta = 127;
|
||||
|
||||
transmit_buffer[1] = 0xFF;
|
||||
transmit_buffer[2] = 0xFC ^ (button << 2);
|
||||
transmit_buffer[3] = xdelta;
|
||||
transmit_buffer[4] = ydelta;
|
||||
|
||||
accum_xdelta -= xdelta;
|
||||
accum_ydelta -= ydelta;
|
||||
|
||||
button &= button_post_mask;
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 5;
|
||||
|
||||
clear_timeout = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Mouse_Create(void)
|
||||
{
|
||||
return new InputDevice_Mouse();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Mouse_IDII[4] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
|
||||
{ "right", "Right Button", 1, IDIT_BUTTON, NULL },
|
||||
{ "left", "Left Button", 0, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
11
mednafen/psx/input/mouse.h
Normal file
11
mednafen/psx/input/mouse.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MDFN_PSX_INPUT_MOUSE_H
|
||||
#define __MDFN_PSX_INPUT_MOUSE_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Mouse_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Mouse_IDII[4];
|
||||
|
||||
}
|
||||
#endif
|
397
mednafen/psx/input/multitap.cpp
Normal file
397
mednafen/psx/input/multitap.cpp
Normal file
@ -0,0 +1,397 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "multitap.h"
|
||||
|
||||
/*
|
||||
TODO: PS1 multitap appears to have some internal knowledge of controller IDs, so it won't get "stuck" waiting for data from a controller that'll never
|
||||
come. We currently sort of "cheat" due to how the dsr_pulse_delay stuff works, but in the future we should try to emulate this multitap functionality.
|
||||
|
||||
Also, full-mode read startup and subport controller ID read timing isn't quite right, so we should fix that too.
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes from tests on real thing(not necessarily emulated the same way here):
|
||||
|
||||
Manual port selection read mode:
|
||||
Write 0x01-0x04 instead of 0x01 as first byte, selects port(1=A,2=B,3=C,4=D) to access.
|
||||
|
||||
Ports that don't exist(0x00, 0x05-0xFF) or don't have a device plugged in will not respond(no DSR pulse).
|
||||
|
||||
Full read mode:
|
||||
Bit0 of third byte(from-zero-index=0x02) should be set to 1 to enter full read mode, on subsequent reads.
|
||||
|
||||
Appears to require a controller to be plugged into the port specified by the first byte as per manual port selection read mode,
|
||||
to write the byte necessary to enter full-read mode; but once the third byte with the bit set has been written, no controller in
|
||||
that port is required for doing full reads(and the manual port selection is ignored when doing a full read).
|
||||
|
||||
However, if there are no controllers plugged in, the returned data will be short:
|
||||
% 0: 0xff
|
||||
% 1: 0x80
|
||||
% 2: 0x5a
|
||||
|
||||
Example full-read bytestream(with controllers plugged into port A, port B, and port C, with port D empty):
|
||||
% 0: 0xff
|
||||
% 1: 0x80
|
||||
% 2: 0x5a
|
||||
|
||||
% 3: 0x73 (Port A controller data start)
|
||||
% 4: 0x5a
|
||||
% 5: 0xff
|
||||
% 6: 0xff
|
||||
% 7: 0x80
|
||||
% 8: 0x8c
|
||||
% 9: 0x79
|
||||
% 10: 0x8f
|
||||
|
||||
% 11: 0x53 (Port B controller data start)
|
||||
% 12: 0x5a
|
||||
% 13: 0xff
|
||||
% 14: 0xff
|
||||
% 15: 0x80
|
||||
% 16: 0x80
|
||||
% 17: 0x75
|
||||
% 18: 0x8e
|
||||
|
||||
% 19: 0x41 (Port C controller data start)
|
||||
% 20: 0x5a
|
||||
% 21: 0xff
|
||||
% 22: 0xff
|
||||
% 23: 0xff
|
||||
% 24: 0xff
|
||||
% 25: 0xff
|
||||
% 26: 0xff
|
||||
|
||||
% 27: 0xff (Port D controller data start)
|
||||
% 28: 0xff
|
||||
% 29: 0xff
|
||||
% 30: 0xff
|
||||
% 31: 0xff
|
||||
% 32: 0xff
|
||||
% 33: 0xff
|
||||
% 34: 0xff
|
||||
|
||||
*/
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice_Multitap::InputDevice_Multitap()
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i] = NULL;
|
||||
mc_devices[i] = NULL;
|
||||
}
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Multitap::~InputDevice_Multitap()
|
||||
{
|
||||
}
|
||||
|
||||
void InputDevice_Multitap::SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device)
|
||||
{
|
||||
assert(sub_index < 4);
|
||||
|
||||
//printf("%d\n", sub_index);
|
||||
|
||||
pad_devices[sub_index] = device;
|
||||
mc_devices[sub_index] = mc_device;
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Multitap::Power(void)
|
||||
{
|
||||
selected_device = -1;
|
||||
bit_counter = 0;
|
||||
receive_buffer = 0;
|
||||
byte_counter = 0;
|
||||
|
||||
mc_mode = false;
|
||||
full_mode = false;
|
||||
full_mode_setting = false;
|
||||
|
||||
fm_dp = 0;
|
||||
memset(fm_buffer, 0, sizeof(fm_buffer));
|
||||
fm_deferred_error_temp = false;
|
||||
fm_deferred_error = false;
|
||||
fm_command_error = false;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(pad_devices[i])
|
||||
pad_devices[i]->Power();
|
||||
|
||||
if(mc_devices[i])
|
||||
mc_devices[i]->Power();
|
||||
}
|
||||
}
|
||||
|
||||
void InputDevice_Multitap::SetDTR(bool new_dtr)
|
||||
{
|
||||
bool old_dtr = dtr;
|
||||
dtr = new_dtr;
|
||||
|
||||
if(!dtr)
|
||||
{
|
||||
if(old_dtr)
|
||||
{
|
||||
//printf("Multitap stop.\n");
|
||||
}
|
||||
|
||||
bit_counter = 0;
|
||||
receive_buffer = 0;
|
||||
selected_device = -1;
|
||||
mc_mode = false;
|
||||
full_mode = false;
|
||||
}
|
||||
|
||||
if(!old_dtr && dtr)
|
||||
{
|
||||
full_mode = full_mode_setting;
|
||||
|
||||
byte_counter = 0;
|
||||
|
||||
//if(full_mode)
|
||||
// printf("Multitap start: %d\n", full_mode);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i]->SetDTR(dtr);
|
||||
mc_devices[i]->SetDTR(dtr);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDevice_Multitap::GetDSR(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Multitap::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
bool ret = 1;
|
||||
int32 tmp_pulse_delay[2][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
|
||||
|
||||
//printf("Receive bit: %d\n", TxD);
|
||||
//printf("TxD %d\n", TxD);
|
||||
|
||||
receive_buffer &= ~ (1 << bit_counter);
|
||||
receive_buffer |= TxD << bit_counter;
|
||||
|
||||
if(1)
|
||||
{
|
||||
if(byte_counter == 0)
|
||||
{
|
||||
bool mangled_txd = TxD;
|
||||
|
||||
if(bit_counter < 4)
|
||||
mangled_txd = (0x01 >> bit_counter) & 1;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i]->Clock(mangled_txd, tmp_pulse_delay[0][i]);
|
||||
mc_devices[i]->Clock(mangled_txd, tmp_pulse_delay[1][i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == 1)
|
||||
{
|
||||
ret = (0x80 >> bit_counter) & 1;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
fm_buffer[i][0] &= (pad_devices[i]->Clock(TxD, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter));
|
||||
}
|
||||
}
|
||||
else if(byte_counter == 2)
|
||||
{
|
||||
ret = (0x5A >> bit_counter) & 1;
|
||||
}
|
||||
// || byte_counter == (0x03 + 0x08 * 1) || byte_counter == (0x03 + 0x08 * 2) || byte_counter == (0x03 + 0x08 * 3))
|
||||
else if(byte_counter >= 0x03 && byte_counter < 0x03 + 0x08 * 4)
|
||||
{
|
||||
if(!fm_command_error && byte_counter >= (0x03 + 1) && byte_counter < (0x03 + 0x08))
|
||||
{
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
fm_buffer[i][byte_counter - 0x03] &= (pad_devices[i]->Clock(0, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter));
|
||||
}
|
||||
}
|
||||
ret &= ((&fm_buffer[0][0])[byte_counter - 0x03] >> bit_counter) & 1;
|
||||
}
|
||||
}
|
||||
else // to if(full_mode)
|
||||
{
|
||||
if((unsigned)selected_device < 4)
|
||||
{
|
||||
ret &= pad_devices[selected_device]->Clock(TxD, tmp_pulse_delay[0][selected_device]);
|
||||
ret &= mc_devices[selected_device]->Clock(TxD, tmp_pulse_delay[1][selected_device]);
|
||||
}
|
||||
}
|
||||
} // end else to if(byte_counter == 0)
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
bit_counter = (bit_counter + 1) & 0x7;
|
||||
if(bit_counter == 0)
|
||||
{
|
||||
//printf("Receive: 0x%02x\n", receive_buffer);
|
||||
if(byte_counter == 0)
|
||||
{
|
||||
mc_mode = (bool)(receive_buffer & 0xF0);
|
||||
if(mc_mode)
|
||||
full_mode = false;
|
||||
|
||||
//printf("Zoomba: 0x%02x\n", receive_buffer);
|
||||
//printf("Full mode: %d %d %d\n", full_mode, bit_counter, byte_counter);
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
memset(fm_buffer, 0xFF, sizeof(fm_buffer));
|
||||
selected_device = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf("Device select: %02x\n", receive_buffer);
|
||||
fm_deferred_error = false;
|
||||
selected_device = ((receive_buffer & 0xF) - 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte_counter == 1)
|
||||
{
|
||||
command = receive_buffer;
|
||||
|
||||
//printf("Multitap sub-command: %02x\n", command);
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
if(command != 0x42)
|
||||
fm_command_error = true;
|
||||
else
|
||||
fm_command_error = fm_deferred_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
fm_command_error = false;
|
||||
}
|
||||
fm_deferred_error = false;
|
||||
}
|
||||
|
||||
if((!mc_mode || full_mode) && byte_counter == 2)
|
||||
{
|
||||
//printf("Full mode setting: %02x\n", receive_buffer);
|
||||
full_mode_setting = receive_buffer & 0x01;
|
||||
}
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == (3 + 8 * 0) || byte_counter == (3 + 8 * 1) || byte_counter == (3 + 8 * 2) || byte_counter == (3 + 8 * 3))
|
||||
{
|
||||
unsigned index = (byte_counter - 3) >> 3;
|
||||
assert(index < 4);
|
||||
|
||||
if(index == 0)
|
||||
fm_deferred_error_temp = false;
|
||||
|
||||
if((fm_dp & (1U << index)) && receive_buffer != 0x42)
|
||||
{
|
||||
//printf("Multitap command check failed: %u, 0x%02x\n", byte_counter, receive_buffer);
|
||||
fm_deferred_error_temp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte_counter == 33)
|
||||
fm_deferred_error = fm_deferred_error_temp;
|
||||
}
|
||||
|
||||
// Handle DSR stuff
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == 0) // Next byte: 0x80
|
||||
{
|
||||
dsr_pulse_delay = 1000;
|
||||
|
||||
fm_dp = 0;
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
fm_dp |= (((bool)(tmp_pulse_delay[0][i])) << i);
|
||||
}
|
||||
else if(byte_counter == 1) // Next byte: 0x5A
|
||||
dsr_pulse_delay = 0x40;
|
||||
else if(byte_counter == 2) // Next byte(typically, controller-dependent): 0x41
|
||||
{
|
||||
if(fm_dp)
|
||||
dsr_pulse_delay = 0x40;
|
||||
else
|
||||
dsr_pulse_delay = 0;
|
||||
}
|
||||
else if(byte_counter >= 3 && byte_counter < 34) // Next byte when byte_counter==3 (typically, controller-dependent): 0x5A
|
||||
{
|
||||
if(byte_counter < 10)
|
||||
{
|
||||
int d = 0x40;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
if(tmp_pulse_delay[0][i] > d)
|
||||
d = tmp_pulse_delay[0][i];
|
||||
|
||||
dsr_pulse_delay = d;
|
||||
}
|
||||
else
|
||||
dsr_pulse_delay = 0x20;
|
||||
|
||||
if(byte_counter == 3 && fm_command_error)
|
||||
dsr_pulse_delay = 0;
|
||||
}
|
||||
} // end if(full_mode)
|
||||
else
|
||||
{
|
||||
if((unsigned)selected_device < 4)
|
||||
{
|
||||
dsr_pulse_delay = std::max<int32>(tmp_pulse_delay[0][selected_device], tmp_pulse_delay[1][selected_device]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//printf("Byte Counter Increment\n");
|
||||
if(byte_counter < 255)
|
||||
byte_counter++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
}
|
52
mednafen/psx/input/multitap.h
Normal file
52
mednafen/psx/input/multitap.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef __MDFN_PSX_INPUT_MULTITAP_H
|
||||
#define __MDFN_PSX_INPUT_MULTITAP_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Multitap : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Multitap();
|
||||
virtual ~InputDevice_Multitap();
|
||||
virtual void Power(void);
|
||||
|
||||
void SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
InputDevice *pad_devices[4];
|
||||
InputDevice *mc_devices[4];
|
||||
|
||||
bool dtr;
|
||||
|
||||
int selected_device;
|
||||
bool full_mode_setting;
|
||||
|
||||
bool full_mode;
|
||||
bool mc_mode;
|
||||
|
||||
uint8 fm_dp; // Device-present.
|
||||
uint8 fm_buffer[4][8];
|
||||
|
||||
bool fm_deferred_error_temp;
|
||||
bool fm_deferred_error;
|
||||
bool fm_command_error;
|
||||
|
||||
uint8 command;
|
||||
uint8 receive_buffer;
|
||||
uint8 bit_counter;
|
||||
uint8 byte_counter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
259
mednafen/psx/input/negcon.cpp
Normal file
259
mednafen/psx/input/negcon.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "negcon.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_neGcon : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_neGcon(void);
|
||||
virtual ~InputDevice_neGcon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
uint8 twist;
|
||||
uint8 anabuttons[3];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[8];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_neGcon::InputDevice_neGcon(void)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_neGcon::~InputDevice_neGcon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_neGcon::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
twist = 0;
|
||||
anabuttons[0] = 0;
|
||||
anabuttons[1] = 0;
|
||||
anabuttons[2] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
void InputDevice_neGcon::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
|
||||
twist = ((32768 + MDFN_de32lsb((const uint8 *)data + 4) - (((int32)MDFN_de32lsb((const uint8 *)data + 8) * 32768 + 16383) / 32767)) * 255 + 32767) / 65535;
|
||||
|
||||
anabuttons[0] = (MDFN_de32lsb((const uint8 *)data + 12) * 255 + 16383) / 32767;
|
||||
anabuttons[1] = (MDFN_de32lsb((const uint8 *)data + 16) * 255 + 16383) / 32767;
|
||||
anabuttons[2] = (MDFN_de32lsb((const uint8 *)data + 20) * 255 + 16383) / 32767;
|
||||
|
||||
//printf("%02x %02x %02x %02x\n", twist, anabuttons[0], anabuttons[1], anabuttons[2]);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_neGcon::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_neGcon::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_neGcon::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x23;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
dsr_pulse_delay = 256;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_buffer[3] = twist; // Twist, 0x00 through 0xFF, 0x80 center.
|
||||
transmit_buffer[4] = anabuttons[0]; // Analog button I, 0x00 through 0xFF, 0x00 = no pressing, 0xFF = max.
|
||||
transmit_buffer[5] = anabuttons[1]; // Analog button II, ""
|
||||
transmit_buffer[6] = anabuttons[2]; // Left shoulder analog button, ""
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
dsr_pulse_delay = 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(transmit_count > 0)
|
||||
dsr_pulse_delay = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_neGcon_Create(void)
|
||||
{
|
||||
return new InputDevice_neGcon();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_neGcon_IDII[21] =
|
||||
{
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "start", "START", 4, IDIT_BUTTON, NULL },
|
||||
{ "up", "D-Pad UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "D-Pad RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "D-Pad DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "D-Pad LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "r", "Right Shoulder", 12, IDIT_BUTTON },
|
||||
|
||||
{ "b", "B", 9, IDIT_BUTTON, NULL },
|
||||
{ "a", "A", 10, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "twist_cwise", "Twist ↓|↑ (Analog, Turn Right)", 6, IDIT_BUTTON_ANALOG },
|
||||
{ "twist_ccwise", "Twist ↑|↓ (Analog, Turn Left)", 5, IDIT_BUTTON_ANALOG },
|
||||
{ "i", "I (Analog)", 8, IDIT_BUTTON_ANALOG },
|
||||
{ "ii", "II (Analog)", 7, IDIT_BUTTON_ANALOG },
|
||||
|
||||
{ "l", "Left Shoulder (Analog)", 11, IDIT_BUTTON_ANALOG },
|
||||
};
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user