diff --git a/src/main/java/mcd/MegaCd.java b/src/main/java/mcd/MegaCd.java
index 652f84a5..d28ba270 100644
--- a/src/main/java/mcd/MegaCd.java
+++ b/src/main/java/mcd/MegaCd.java
@@ -20,6 +20,7 @@
package mcd;
import mcd.bus.McdSubInterruptHandler;
+import mcd.cart.MegaCdCartInfoProvider;
import mcd.cdd.ExtendedCueSheet;
import mcd.util.McdMemView;
import omegadrive.SystemLoader;
@@ -36,7 +37,6 @@
import omegadrive.memory.MemoryProvider;
import omegadrive.savestate.BaseStateHandler;
import omegadrive.system.BaseSystem;
-import omegadrive.system.Megadrive;
import omegadrive.system.SysUtil;
import omegadrive.system.SystemProvider;
import omegadrive.ui.DisplayWindow;
@@ -239,16 +239,14 @@ private double getMicrosPerTick() {
return 1_000_000.0 / (mclkhz / (FM_DIVIDER * MCLK_DIVIDER));
}
- @Override
- protected RegionDetector.Region getRomRegion() {
- return RegionDetector.detectRegion((MdCartInfoProvider) romContext.cartridgeInfoProvider);
- }
-
@Override
protected RomContext createRomContext(SysUtil.RomSpec rom) {
- romContext = Megadrive.createRomContext(rom, memory, emuFrame.getRegionOverride());
assert !ZipUtil.isCompressedByteStream(rom.file);
- return romContext;
+ RomContext rc = new RomContext(rom);
+ MdCartInfoProvider mcip = MegaCdCartInfoProvider.createMcdInstance(memory, rc);
+ rc.cartridgeInfoProvider = mcip;
+ rc.region = RegionDetector.selectRegion(display, mcip);
+ return rc;
}
@Override
diff --git a/src/main/java/mcd/cart/MegaCdCartInfoProvider.java b/src/main/java/mcd/cart/MegaCdCartInfoProvider.java
new file mode 100644
index 00000000..f9a9b653
--- /dev/null
+++ b/src/main/java/mcd/cart/MegaCdCartInfoProvider.java
@@ -0,0 +1,129 @@
+package mcd.cart;
+
+import mcd.cdd.CdModel;
+import omegadrive.cart.MdCartInfoProvider;
+import omegadrive.memory.IMemoryProvider;
+import omegadrive.system.SysUtil;
+import omegadrive.system.SystemProvider;
+import omegadrive.util.LogHelper;
+import omegadrive.util.RegionDetector;
+import omegadrive.util.Util;
+import omegadrive.vdp.util.MemView;
+import org.slf4j.Logger;
+
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Federico Berti
+ *
+ * Copyright 2024
+ */
+public class MegaCdCartInfoProvider extends MdCartInfoProvider {
+
+ private final static Logger LOG = LogHelper.getLogger(MegaCdCartInfoProvider.class.getSimpleName());
+
+ private final static int headerLen = 0x10;
+
+ private final static int TRACK01_SECURITY_CODE_START = 0x200;
+ private final static byte[] SCD_SYS_BYTES = "SEGADISCSYSTEM".getBytes();
+ private final static byte ff = (byte) 0xff;
+ private final static byte[] CD_SYNC_BYTES = {0x00, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, 0x00};
+
+ enum SecurityCodeInfo {
+ USA(RegionDetector.Region.USA, "d7b7e8e00cee68e15828197ee5090f5a14acd436", 0x584),
+ EU(RegionDetector.Region.EUROPE, "4a1946f9aaf7261d9d4ccb0556b7342eaf1cf2f8", 0x56E),
+ JP(RegionDetector.Region.JAPAN, "ffe78f1ffdb8b76b358eb8a29b1d5f9dbeabacca", 0x156);
+ public final RegionDetector.Region region;
+ public final String sha1;
+ public final int length;
+
+ SecurityCodeInfo(RegionDetector.Region region, String sha1, int length) {
+ this.region = region;
+ this.sha1 = sha1;
+ this.length = length;
+ }
+ }
+
+
+ public SysUtil.RomFileType detectedRomFileType;
+ public RegionDetector.Region securityCodeRegion;
+
+
+ public static MegaCdCartInfoProvider createMcdInstance(IMemoryProvider memoryProvider, SystemProvider.RomContext rom) {
+ MegaCdCartInfoProvider m = new MegaCdCartInfoProvider(memoryProvider, rom);
+ m.init();
+ return m;
+ }
+
+ private MegaCdCartInfoProvider(IMemoryProvider memoryProvider, SystemProvider.RomContext rom) {
+ super(memoryProvider, rom);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ CdModel.ExtendedTrackData t1 = romContext.sheet.extTracks.get(0);
+ checkTrack01Header(t1);
+ securityCodeRegion = verifySecurityCodeRegion(t1);
+ }
+
+ private void checkTrack01Header(CdModel.ExtendedTrackData track01) {
+ //check that *.iso is really an iso file internally
+ SysUtil.RomFileType romFileType = romContext.sheet.romFileType;
+ try {
+ byte[] header = new byte[headerLen];
+ RandomAccessFile raf = track01.file;
+ raf.seek(0);
+ CdModel.TrackDataType trackDataType = track01.trackDataType;
+ raf.read(header, 0, header.length);
+ detectedRomFileType = SysUtil.RomFileType.UNKNOWN;
+ if (Arrays.equals(SCD_SYS_BYTES, 0, SCD_SYS_BYTES.length, header, 0, SCD_SYS_BYTES.length)) {
+ System.out.println("valid Sega CD image");
+ detectedRomFileType = SysUtil.RomFileType.ISO;
+ } else if (Arrays.equals(CD_SYNC_BYTES, 0, CD_SYNC_BYTES.length, header, 0, CD_SYNC_BYTES.length)) {
+ System.out.println("CD-ROM synchro pattern");
+ detectedRomFileType = SysUtil.RomFileType.BIN_CUE;
+ } else if (trackDataType == CdModel.TrackDataType.AUDIO) {
+ System.out.println("CD-AUDIO");
+ detectedRomFileType = SysUtil.RomFileType.BIN_CUE;
+ }
+ assert detectedRomFileType == romFileType : detectedRomFileType + " vs " + romFileType;
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOG.error(e.getMessage());
+ }
+ }
+
+ private static RegionDetector.Region verifySecurityCodeRegion(CdModel.ExtendedTrackData track01) {
+ try {
+ byte[] secCode = new byte[0x700];
+ //sector 2352 starts with 0x10 sync/header bytes, ignore them
+ int secCodeStart = track01.trackDataType.size == CdModel.SectorSize.S_2048 ?
+ TRACK01_SECURITY_CODE_START : TRACK01_SECURITY_CODE_START + 0x10;
+ RandomAccessFile raf = track01.file;
+ raf.seek(secCodeStart);
+ raf.read(secCode);
+ ByteBuffer bb = ByteBuffer.wrap(secCode);
+ for (SecurityCodeInfo sci : SecurityCodeInfo.values()) {
+ byte[] b = new byte[sci.length];
+ bb.position(0);
+ bb.get(b);
+ String sha1 = Util.computeSha1Sum(b);
+ if (sci.sha1.equalsIgnoreCase(sha1)) {
+ System.out.println(sci.region);
+ return sci.region;
+ }
+ }
+ LOG.error("Unknown security code!");
+ StringBuilder sb = new StringBuilder(track01 + "\n");
+ MemView.fillFormattedString(sb, secCode, 0, secCode.length);
+ System.out.println(sb);
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOG.error(e.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/mcd/cdd/ExtendedCueSheet.java b/src/main/java/mcd/cdd/ExtendedCueSheet.java
index e3bad907..16c5d24e 100644
--- a/src/main/java/mcd/cdd/ExtendedCueSheet.java
+++ b/src/main/java/mcd/cdd/ExtendedCueSheet.java
@@ -10,7 +10,10 @@
import org.digitalmediaserver.cuelib.TrackData;
import org.slf4j.Logger;
-import java.io.*;
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
@@ -28,13 +31,6 @@ public class ExtendedCueSheet implements Closeable {
private final static Logger LOG = LogHelper.getLogger(ExtendedCueSheet.class.getSimpleName());
- private final static byte[] SCD_SYS_BYTES = "SEGADISCSYSTEM".getBytes();
- private final static byte ff = (byte) 0xff;
- private final static byte[] CD_SYNC_BYTES = {0x00, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, 0x00};
-
- //TODO remove at some point
- private static final boolean CUE_TEST_MODE = false;
- private static final Path NO_BIN_FILE_PATH = Paths.get(".", "test.bin");
//placeholder name
private static final String TEMPLATE_ISO_NAME_PH = "";
@@ -52,13 +48,6 @@ public class ExtendedCueSheet implements Closeable {
public final List extTracks = new ArrayList<>();
public int numTracks, sectorEnd;
protected final Map fileCache = new HashMap<>();
- private static RandomAccessFile NO_BIN_FILE;
-
- static {
- if (CUE_TEST_MODE) {
- LOG.warn("Cue test mode: {}", CUE_TEST_MODE);
- }
- }
public ExtendedCueSheet(Path discImage, RomFileType rft) {
assert rft.isDiscImage();
@@ -107,35 +96,11 @@ private void parseCueSheet() {
List tracks = extCueSheet.cueSheet.getAllTrackData();
assert !tracks.isEmpty();
extCueSheet.numTracks = tracks.size();
- checkTrack01Header(tracks.get(0));
for (TrackData track : tracks) {
parseTrack(extCueSheet, track.getNumber(), cuePath);
}
}
- private void checkTrack01Header(TrackData track01) {
- try {
- byte[] header = new byte[16];
- RandomAccessFile raf = getDataFile(this, track01.getParent().getFile(), cuePath);
- TrackDataType trackDataType = TrackDataType.parse(track01.getDataType());
- raf.read(header, 0, header.length);
- RomFileType detected = RomFileType.UNKNOWN;
- if (Arrays.equals(SCD_SYS_BYTES, 0, SCD_SYS_BYTES.length, header, 0, SCD_SYS_BYTES.length)) {
- System.out.println("valid Sega CD image");
- detected = RomFileType.ISO;
- } else if (Arrays.equals(CD_SYNC_BYTES, 0, CD_SYNC_BYTES.length, header, 0, CD_SYNC_BYTES.length)) {
- System.out.println("CD-ROM synchro pattern");
- detected = RomFileType.BIN_CUE;
- } else if (trackDataType == TrackDataType.AUDIO) {
- System.out.println("CD-AUDIO");
- detected = RomFileType.BIN_CUE;
- }
- assert detected == romFileType : detected + " vs " + romFileType;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
private void parseTrack(ExtendedCueSheet extCueSheet, int trackNumber, Path cuePath) {
TrackData trackData = getTrack(extCueSheet.cueSheet, trackNumber);
RandomAccessFile raf = getDataFile(extCueSheet, trackData.getParent().getFile(), cuePath);
@@ -171,15 +136,12 @@ private void parseTrack(ExtendedCueSheet extCueSheet, int trackNumber, Path cueP
}
private static RandomAccessFile getDataFile(ExtendedCueSheet extCueSheet, String key, Path file, Path cuePath) {
+ //TODO close RandomAccessFile
if (!extCueSheet.fileCache.containsKey(key)) {
try {
extCueSheet.fileCache.put(key, new RandomAccessFile(file.toFile(), "r"));
} catch (Exception e) {
- if (CUE_TEST_MODE) {
- handleTestCueMode(extCueSheet, cuePath, key);
- } else {
- e.printStackTrace();
- }
+ e.printStackTrace();
}
}
return extCueSheet.fileCache.get(key);
@@ -191,21 +153,6 @@ private static RandomAccessFile getDataFile(ExtendedCueSheet extCueSheet, String
return getDataFile(extCueSheet, key, fp, cuePath);
}
- private static void handleTestCueMode(ExtendedCueSheet extCueSheet, Path cuePath, String key) {
- try {
- if (NO_BIN_FILE == null) {
- Path p = cuePath.resolveSibling(NO_BIN_FILE_PATH);
- NO_BIN_FILE = new RandomAccessFile(p.toFile(), "r");
- LOG.warn("Missing bin file: {}, using instead: {}, cue_test_mode: {}", key, p.toAbsolutePath(), CUE_TEST_MODE);
- }
- extCueSheet.fileCache.put(key, NO_BIN_FILE);
- extCueSheet.romFileType = RomFileType.BIN_CUE;
- } catch (FileNotFoundException ex) {
- LOG.error("Missing bin file: {}", key);
- throw new RuntimeException(ex);
- }
- }
-
private static TrackData getTrack(CueSheet cueSheet, int number) {
assert number > 0;
TrackData td = cueSheet.getAllTrackData().get(number - 1);
diff --git a/src/main/java/omegadrive/cart/MdCartInfoProvider.java b/src/main/java/omegadrive/cart/MdCartInfoProvider.java
index 01cb12dc..9099c960 100644
--- a/src/main/java/omegadrive/cart/MdCartInfoProvider.java
+++ b/src/main/java/omegadrive/cart/MdCartInfoProvider.java
@@ -23,6 +23,7 @@
import omegadrive.cart.loader.MdRomDbModel;
import omegadrive.cart.mapper.md.MdMapperType;
import omegadrive.memory.IMemoryProvider;
+import omegadrive.memory.MemoryProvider;
import omegadrive.system.SysUtil;
import omegadrive.util.LogHelper;
import omegadrive.util.Size;
@@ -190,12 +191,17 @@ public String toString() {
return sb.append(headerInfo).toString();
}
+ public static final MdCartInfoProvider NO_PROVIDER = new MdCartInfoProvider(MemoryProvider.NO_MEMORY, RomContext.NO_ROM);
+
public static MdCartInfoProvider createMdInstance(IMemoryProvider memoryProvider, RomContext rom) {
- MdCartInfoProvider provider = new MdCartInfoProvider();
- provider.memoryProvider = memoryProvider;
- provider.romContext = rom;
- provider.init();
- return provider;
+ MdCartInfoProvider m = new MdCartInfoProvider(memoryProvider, rom);
+ m.init();
+ return m;
+ }
+
+ protected MdCartInfoProvider(IMemoryProvider memoryProvider, RomContext rom) {
+ this.memoryProvider = memoryProvider;
+ this.romContext = rom;
}
public boolean isSramUsedWithBrokenHeader(long address) {
diff --git a/src/main/java/omegadrive/sound/msumd/CueFileParser.java b/src/main/java/omegadrive/sound/msumd/CueFileParser.java
index 8b8d2375..b25f4821 100644
--- a/src/main/java/omegadrive/sound/msumd/CueFileParser.java
+++ b/src/main/java/omegadrive/sound/msumd/CueFileParser.java
@@ -1,21 +1,3 @@
-/*
- * Copyright (C) 2003, 2014 Graham Sanderson
- *
- * This file is part of JPSX.
- *
- * JPSX 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 3 of the License, or
- * (at your option) any later version.
- *
- * JPSX 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 JPSX. If not, see .
- */
package omegadrive.sound.msumd;
import omegadrive.util.LogHelper;
diff --git a/src/main/java/omegadrive/system/BaseSystem.java b/src/main/java/omegadrive/system/BaseSystem.java
index 5504fec1..cce84a19 100644
--- a/src/main/java/omegadrive/system/BaseSystem.java
+++ b/src/main/java/omegadrive/system/BaseSystem.java
@@ -24,6 +24,7 @@
import omegadrive.SystemLoader.SystemType;
import omegadrive.UserConfigHolder;
import omegadrive.bus.model.BaseBusProvider;
+import omegadrive.cart.CartridgeInfoProvider;
import omegadrive.input.InputProvider;
import omegadrive.input.KeyboardInput;
import omegadrive.joypad.JoypadProvider;
@@ -36,7 +37,6 @@
import omegadrive.ui.DisplayWindow;
import omegadrive.ui.PrefStore;
import omegadrive.util.*;
-import omegadrive.util.RegionDetector.Region;
import omegadrive.vdp.model.BaseVdpAdapterEventSupport.VdpEventListener;
import omegadrive.vdp.model.BaseVdpProvider;
import org.slf4j.Logger;
@@ -64,11 +64,9 @@ public abstract class BaseSystem implements
protected InputProvider inputProvider;
protected BUS bus;
protected SystemType systemType;
-
- protected VideoMode videoMode = VideoMode.PAL_H40_V30;
protected RomContext romContext = NO_ROM;
protected Future runningRomFuture;
- protected DisplayWindow emuFrame;
+ protected DisplayWindow display;
protected DisplayWindow.DisplayContext displayContext;
@@ -116,21 +114,8 @@ protected void initAfterRomLoad() {
protected abstract void updateVideoMode(boolean force);
- protected Region getRomRegion() {
- return Region.JAPAN;
- }
-
- public static Region getRegionInternal(String regionOvr, Region romRegion) {
- Region ovrRegion = RegionDetector.getRegion(regionOvr);
- if (ovrRegion != null && ovrRegion != romRegion) {
- LOG.info("Setting region override from: {} to {}", romRegion, ovrRegion);
- romRegion = ovrRegion;
- }
- return romRegion;
- }
-
protected BaseSystem(DisplayWindow emuFrame) {
- this.emuFrame = emuFrame;
+ this.display = emuFrame;
}
@Override
@@ -152,7 +137,7 @@ public void handleSystemEvent(SystemEvent event, Object parameter) {
handleSaveState((Path) parameter);
break;
case TOGGLE_FULL_SCREEN:
- emuFrame.setFullScreen((Boolean) parameter);
+ display.setFullScreen((Boolean) parameter);
break;
case TOGGLE_PAUSE:
handlePause();
@@ -192,8 +177,8 @@ protected void handleSoftReset() {
}
protected void reloadWindowState() {
- emuFrame.addKeyListener(KeyboardInput.createKeyAdapter(systemType, joypad));
- emuFrame.reloadControllers(inputProvider.getAvailableControllers());
+ display.addKeyListener(KeyboardInput.createKeyAdapter(systemType, joypad));
+ display.reloadControllers(inputProvider.getAvailableControllers());
}
public void handleNewRom(RomSpec romSpec) {
@@ -206,7 +191,7 @@ public void handleNewRom(RomSpec romSpec) {
private void handleCloseApp() {
try {
handleCloseRom();
- emuFrame.close();
+ display.close();
sound.close();
Util.executorService.shutdown();
Util.executorService.awaitTermination(1, TimeUnit.SECONDS);
@@ -309,12 +294,13 @@ private void handleRomInternal() {
Util.sleep(100);
}
LOG.info("Rom thread cancel");
- emuFrame.resetScreen();
+ display.resetScreen();
sound.reset();
bus.closeRom();
telemetry.reset();
Optional.ofNullable(vdp).ifPresent(Device::reset);
cycleCounter = 1;
+ romContext = NO_ROM;
}
}
@@ -374,9 +360,10 @@ public void run() {
return;
}
memory.setRomData(data);
+ assert romContext == NO_ROM;
romContext = createRomContext(romSpec);
String romName = FileUtil.getFileName(romSpec.file);
- emuFrame.setRomData(romContext);
+ display.setRomData(romContext);
Thread.currentThread().setName(threadNamePrefix + romName);
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 1);
LOG.info("Running rom: {},\n{}", romName, romContext);
@@ -395,7 +382,8 @@ public void run() {
protected RomContext createRomContext(RomSpec rom) {
RomContext rc = new RomContext(rom);
- rc.region = getRegionInternal(emuFrame.getRegionOverride(), getRomRegion());
+ rc.cartridgeInfoProvider = CartridgeInfoProvider.createInstance(memory, rom.file);
+ rc.region = RegionDetector.selectRegion(display, rc.cartridgeInfoProvider);
return rc;
}
@@ -408,7 +396,7 @@ protected void handleVdpDumpScreenData() {
protected void doRendering(int[] data) {
displayContext.data = data;
- emuFrame.renderScreenLinear(displayContext);
+ display.renderScreenLinear(displayContext);
}
private void handlePause() {
diff --git a/src/main/java/omegadrive/system/Genesis.java b/src/main/java/omegadrive/system/Genesis.java
deleted file mode 100644
index 5e091501..00000000
--- a/src/main/java/omegadrive/system/Genesis.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Genesis
- * Copyright (c) 2018-2019 Federico Berti
- * Last modified: 26/10/19 15:29
- *
- * 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 3 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, see .
- */
-
-package omegadrive.system;
-
-import omegadrive.SystemLoader;
-import omegadrive.bus.md.GenesisBus;
-import omegadrive.bus.md.SvpMapper;
-import omegadrive.bus.model.GenesisBusProvider;
-import omegadrive.cart.MdCartInfoProvider;
-import omegadrive.cpu.m68k.M68kProvider;
-import omegadrive.cpu.m68k.MC68000Wrapper;
-import omegadrive.cpu.ssp16.Ssp16;
-import omegadrive.cpu.z80.Z80CoreWrapper;
-import omegadrive.cpu.z80.Z80Provider;
-import omegadrive.input.InputProvider;
-import omegadrive.joypad.GenesisJoypad;
-import omegadrive.memory.MemoryProvider;
-import omegadrive.savestate.BaseStateHandler;
-import omegadrive.sound.SoundProvider;
-import omegadrive.ui.DisplayWindow;
-import omegadrive.util.LogHelper;
-import omegadrive.util.RegionDetector;
-import omegadrive.util.RegionDetector.Region;
-import omegadrive.util.Util;
-import omegadrive.vdp.model.BaseVdpProvider;
-import omegadrive.vdp.model.GenesisVdpProvider;
-import omegadrive.vdp.util.MemView;
-import omegadrive.vdp.util.UpdatableViewer;
-import org.slf4j.Logger;
-
-/**
- * Megadrive main class
- *
- * @author Federico Berti
- *
- * @Deprecated unused, to be removed
- */
-@Deprecated
-public class Genesis extends BaseSystem {
-
- public final static boolean verbose = false;
- //the emulation runs at MCLOCK_MHZ/MCLK_DIVIDER
- public final static int MCLK_DIVIDER = 7;
- protected final static double VDP_RATIO = 4.0 / MCLK_DIVIDER; //16 -> MCLK/4, 20 -> MCLK/5
- protected final static int M68K_DIVIDER = 7 / MCLK_DIVIDER;
- public final static double[] vdpVals = {VDP_RATIO * BaseVdpProvider.MCLK_DIVIDER_FAST_VDP, VDP_RATIO * BaseVdpProvider.MCLK_DIVIDER_SLOW_VDP};
- protected final static int Z80_DIVIDER = 14 / MCLK_DIVIDER;
- protected final static int FM_DIVIDER = 42 / MCLK_DIVIDER;
- protected static final int SVP_CYCLES = 100;
- protected static final int SVP_RUN_CYCLES = (int) (SVP_CYCLES * 1.5);
-
- private final static Logger LOG = LogHelper.getLogger(Genesis.class.getSimpleName());
-
- static {
-// System.setProperty("68k.debug", "true");
-// System.setProperty("helios.68k.debug.mode", "2");
-// System.setProperty("z80.debug", "true");
- }
-
- protected Z80Provider z80;
- protected M68kProvider cpu;
- protected Ssp16 ssp16 = Ssp16.NO_SVP;
- protected UpdatableViewer memView = UpdatableViewer.NO_OP_VIEWER;
- protected boolean hasSvp = ssp16 != Ssp16.NO_SVP;
- protected double nextVdpCycle = vdpVals[0];
- protected int next68kCycle = M68K_DIVIDER;
- protected int nextZ80Cycle = Z80_DIVIDER;
- protected int nextFMCycle = FM_DIVIDER;
- protected int nextSvpCycle = SVP_CYCLES;
-
- protected Genesis(DisplayWindow emuFrame) {
- super(emuFrame);
- systemType = SystemLoader.SystemType.GENESIS;
- }
-
- public static SystemProvider createNewInstance(DisplayWindow emuFrame) {
- return new Genesis(emuFrame);
- }
-
- @Override
- public void init() {
- stateHandler = BaseStateHandler.EMPTY_STATE;
- joypad = GenesisJoypad.create(this);
- inputProvider = InputProvider.createInstance(joypad);
-
- memory = MemoryProvider.createGenesisInstance();
- bus = createBus();
- vdp = GenesisVdpProvider.createVdp(bus);
- cpu = MC68000Wrapper.createInstance(bus);
- z80 = Z80CoreWrapper.createInstance(getSystemType(), bus);
- //sound attached later
- sound = SoundProvider.NO_SOUND;
-
- bus.attachDevices(this, memory, joypad, vdp, cpu, z80);
- reloadWindowState();
- createAndAddVdpEventListener();
- }
-
- protected void loop() {
- updateVideoMode(true);
- int cnt;
- do {
- cnt = cycleCounter;
- cnt = runZ80(cnt);
- cnt = run68k(cnt);
- cnt = runFM(cnt);
- if (hasSvp) {
- runSvp(cnt);
- }
- //this should be last as it could change the counter
- cnt = runVdp(cnt);
- cycleCounter = cnt;
- } while (!futureDoneFlag);
- }
-
- private int runSvp(int untilClock) {
- while (nextSvpCycle <= untilClock) {
- ssp16.ssp1601_run(SVP_RUN_CYCLES);
- nextSvpCycle += SVP_CYCLES;
- }
- return untilClock;
- }
-
- private int runVdp(int untilClock) {
- while (nextVdpCycle <= untilClock) {
- int vdpMclk = vdp.runSlot();
- nextVdpCycle += vdpVals[vdpMclk - 4];
- if (cycleCounter == 0) { //counter could be reset to 0 when calling vdp::runSlot
- untilClock = 0;
- }
- }
- return (int) Math.max(untilClock, nextVdpCycle);
- }
-
- protected final int run68k(int untilClock) {
- while (next68kCycle <= untilClock) {
- boolean isRunning = bus.is68kRunning();
- boolean canRun = !cpu.isStopped() && isRunning;
- int cycleDelay = 1;
- if (canRun) {
- cycleDelay = cpu.runInstruction();
- }
- //interrupts are processed after the current instruction
- if (isRunning) {
- bus.handleVdpInterrupts68k();
- }
- cycleDelay = Math.max(1, cycleDelay);
- next68kCycle += M68K_DIVIDER * cycleDelay;
- }
- return untilClock;
- }
-
- private int runZ80(int untilClock) {
- while (nextZ80Cycle <= untilClock) {
- int cycleDelay = 0;
- boolean running = bus.isZ80Running();
- if (running) {
- cycleDelay = z80.executeInstruction();
- bus.handleVdpInterruptsZ80();
- }
- cycleDelay = Math.max(1, cycleDelay);
- nextZ80Cycle += Z80_DIVIDER * cycleDelay;
- }
- return untilClock;
- }
-
- private int runFM(int untilClock) {
- while (nextFMCycle <= untilClock) {
- bus.getFm().tick();
- nextFMCycle += FM_DIVIDER;
- }
- return nextFMCycle;
- }
-
- protected GenesisBusProvider createBus() {
- return new GenesisBus();
- }
-
- @Override
- protected void updateVideoMode(boolean force) {
- if (force || displayContext.videoMode != vdp.getVideoMode()) {
- displayContext.videoMode = vdp.getVideoMode();
- double microsPerTick = getMicrosPerTick();
- sound.getFm().setMicrosPerTick(microsPerTick);
- targetNs = (long) (getRegion().getFrameIntervalMs() * Util.MILLI_IN_NS);
- LOG.info("Video mode changed: {}, microsPerTick: {}", displayContext.videoMode, microsPerTick);
- }
- }
-
- private double getMicrosPerTick() {
- double mclkhz = displayContext.videoMode.isPal() ? Util.GEN_PAL_MCLOCK_MHZ : Util.GEN_NTSC_MCLOCK_MHZ;
- return 1_000_000.0 / (mclkhz / (FM_DIVIDER * MCLK_DIVIDER));
- }
-
- @Override
- protected Region getRomRegion() {
- return RegionDetector.detectRegion((MdCartInfoProvider) romContext.cartridgeInfoProvider);
- }
-
- @Override
- protected RomContext createRomContext(SysUtil.RomSpec rom) {
- return Megadrive.createRomContext(rom, memory, emuFrame.getRegionOverride());
- }
-
- @Override
- public void newFrame() {
- checkSvp();
- memView.update();
- super.newFrame();
- }
-
- private void checkSvp() {
- ssp16 = SvpMapper.ssp16;
- hasSvp = ssp16 != Ssp16.NO_SVP;
- }
-
- protected UpdatableViewer createMemView() {
- return MemView.createInstance(bus, vdp.getVdpMemory());
- }
-
- /**
- * Counters can go negative when the video mode changes
- */
- @Override
- protected void resetCycleCounters(int counter) {
- nextZ80Cycle = Math.max(1, counter - nextZ80Cycle);
- next68kCycle = Math.max(1, counter - next68kCycle);
- nextVdpCycle = Math.max(1, counter - nextVdpCycle);
- nextFMCycle = Math.max(1, counter - nextFMCycle);
- nextSvpCycle = Math.max(1, counter - nextSvpCycle);
- }
-
- @Override
- protected void initAfterRomLoad() {
- super.initAfterRomLoad();
- bus.attachDevice(sound);
- vdp.addVdpEventListener(sound);
- SvpMapper.ssp16 = Ssp16.NO_SVP;
- resetAfterRomLoad();
- memView.reset();
- memView = createMemView();
- }
-
- @Override
- protected void resetAfterRomLoad() {
- super.resetAfterRomLoad();
- cpu.reset();
- z80.reset(); //TODO confirm this is needed
- }
-
- @Override
- protected void handleSoftReset() {
- if (softReset) {
- cpu.softReset();
- }
- super.handleSoftReset();
- }
-}
diff --git a/src/main/java/omegadrive/system/Megadrive.java b/src/main/java/omegadrive/system/Megadrive.java
index 2557f1cb..acc02266 100644
--- a/src/main/java/omegadrive/system/Megadrive.java
+++ b/src/main/java/omegadrive/system/Megadrive.java
@@ -31,7 +31,6 @@
import omegadrive.cpu.z80.Z80Provider;
import omegadrive.input.InputProvider;
import omegadrive.joypad.GenesisJoypad;
-import omegadrive.memory.IMemoryProvider;
import omegadrive.memory.MemoryProvider;
import omegadrive.savestate.BaseStateHandler;
import omegadrive.ui.DisplayWindow;
@@ -45,8 +44,6 @@
import org.slf4j.Logger;
import s32x.util.Md32xRuntimeData;
-import java.util.Optional;
-
import static omegadrive.util.BufferUtil.CpuDeviceAccess.M68K;
import static omegadrive.util.BufferUtil.CpuDeviceAccess.Z80;
@@ -200,24 +197,12 @@ private double getMicrosPerTick() {
return 1_000_000.0 / (mclkhz / (FM_DIVIDER * MCLK_DIVIDER));
}
- @Override
- protected RegionDetector.Region getRomRegion() {
- return RegionDetector.detectRegion((MdCartInfoProvider) romContext.cartridgeInfoProvider);
- }
-
@Override
protected RomContext createRomContext(SysUtil.RomSpec rom) {
- return createRomContext(rom, memory, emuFrame.getRegionOverride());
- }
-
- public static RomContext createRomContext(SysUtil.RomSpec rom, IMemoryProvider memory, String regionOverride) {
RomContext rc = new RomContext(rom);
MdCartInfoProvider mcip = MdCartInfoProvider.createMdInstance(memory, rc);
rc.cartridgeInfoProvider = mcip;
- String regionOvr = Optional.ofNullable(mcip.getEntry().forceRegion).
- orElse(regionOverride);
- RegionDetector.Region romRegion = RegionDetector.detectRegion((MdCartInfoProvider) rc.cartridgeInfoProvider);
- rc.region = getRegionInternal(regionOvr, romRegion);
+ rc.region = RegionDetector.selectRegion(display, mcip);
return rc;
}
diff --git a/src/main/java/omegadrive/system/SystemProvider.java b/src/main/java/omegadrive/system/SystemProvider.java
index 1bbc5f51..30ea4d21 100644
--- a/src/main/java/omegadrive/system/SystemProvider.java
+++ b/src/main/java/omegadrive/system/SystemProvider.java
@@ -23,7 +23,7 @@
import omegadrive.Device;
import omegadrive.SystemLoader;
import omegadrive.cart.CartridgeInfoProvider;
-import omegadrive.cart.MdCartInfoProvider;
+import omegadrive.memory.MemoryProvider;
import omegadrive.system.SysUtil.RomSpec;
import omegadrive.util.RegionDetector;
@@ -42,7 +42,7 @@ class RomContext {
NO_ROM = new RomContext();
NO_ROM.region = RegionDetector.Region.USA;
NO_ROM.romSpec = RomSpec.of(Path.of("NO_PATH"));
- NO_ROM.cartridgeInfoProvider = new MdCartInfoProvider();
+ NO_ROM.cartridgeInfoProvider = CartridgeInfoProvider.createInstance(MemoryProvider.NO_MEMORY, NO_ROM.romSpec.file);
}
public RegionDetector.Region region;
diff --git a/src/main/java/omegadrive/system/gb/Gb.java b/src/main/java/omegadrive/system/gb/Gb.java
index 21205f43..6677ce05 100644
--- a/src/main/java/omegadrive/system/gb/Gb.java
+++ b/src/main/java/omegadrive/system/gb/Gb.java
@@ -15,7 +15,6 @@
import omegadrive.system.SystemProvider;
import omegadrive.ui.DisplayWindow;
import omegadrive.util.LogHelper;
-import omegadrive.util.RegionDetector;
import omegadrive.util.Util;
import omegadrive.util.VideoMode;
import omegadrive.vdp.model.BaseVdpAdapter;
@@ -77,7 +76,7 @@ protected void loop() {
private Emulator createEmulator(String[] args) {
try {
- HeliosDisplay display = new HeliosDisplay(this, emuFrame);
+ HeliosDisplay display = new HeliosDisplay(this, this.display);
emulator = new Emulator(args, display, (SoundOutput) sound.getFm(), controller);
vdp = BaseVdpAdapter.getVdpProviderWrapper(VideoMode.NTSCJ_H20_V18, display);
} catch (Exception e) {
@@ -118,9 +117,4 @@ protected void resetCycleCounters(int counter) {
protected void updateVideoMode(boolean force) {
displayContext.videoMode = vdp.getVideoMode();
}
-
- @Override
- protected RegionDetector.Region getRomRegion() {
- return RegionDetector.Region.USA;
- }
}
diff --git a/src/main/java/omegadrive/util/RegionDetector.java b/src/main/java/omegadrive/util/RegionDetector.java
index 8033fb66..30a04a6d 100644
--- a/src/main/java/omegadrive/util/RegionDetector.java
+++ b/src/main/java/omegadrive/util/RegionDetector.java
@@ -20,7 +20,10 @@
package omegadrive.util;
import com.google.common.base.Strings;
+import mcd.cart.MegaCdCartInfoProvider;
+import omegadrive.cart.CartridgeInfoProvider;
import omegadrive.cart.MdCartInfoProvider;
+import omegadrive.ui.DisplayWindow;
import org.slf4j.Logger;
import java.util.*;
@@ -34,7 +37,7 @@ public class RegionDetector {
public final static int PAL_FPS = 50;
public final static int NTSC_FPS = 60;
- public static Region detectRegion(MdCartInfoProvider cartInfoProvider, boolean verbose) {
+ public static Region detectHeaderRegion(MdCartInfoProvider cartInfoProvider, boolean verbose) {
String s = cartInfoProvider.getRegion();
s = Strings.padEnd(s, 3, ' ');
assert s.length() == 3;
@@ -58,8 +61,53 @@ public static Region detectRegion(MdCartInfoProvider cartInfoProvider, boolean v
return res;
}
- public static Region detectRegion(MdCartInfoProvider cartInfo) {
- return detectRegion(cartInfo, false);
+ public static Region detectHeaderRegion(MdCartInfoProvider cartInfo) {
+ return detectHeaderRegion(cartInfo, false);
+ }
+
+ public static Region selectRegion(DisplayWindow w, CartridgeInfoProvider cip) {
+ assert cip != MdCartInfoProvider.NO_PROVIDER;
+ Region regionFileName = getRegionFileName(cip.getRomName());
+ Region regionOvrUi = RegionDetector.getRegion(w.getRegionOverride());
+ Region regionOvrConfig = null;
+ Region securityCodeRegion = null;
+ Region romHeaderRegion = RegionDetector.getRegion(cip.getRegion());
+ if (cip instanceof MdCartInfoProvider mcip) {
+ regionOvrConfig = RegionDetector.getRegion(Optional.ofNullable((mcip).getEntry().forceRegion).
+ orElse(null));
+ romHeaderRegion = detectHeaderRegion(mcip, false);
+ }
+ if (cip instanceof MegaCdCartInfoProvider mcdip) {
+ securityCodeRegion = mcdip.securityCodeRegion;
+ }
+ if (regionOvrUi != null && regionOvrUi != romHeaderRegion) {
+ LOG.info("Setting region override from UI: {} to {}", romHeaderRegion, regionOvrUi);
+ return regionOvrUi;
+ }
+ if (regionOvrConfig != null && regionOvrConfig != romHeaderRegion) {
+ LOG.info("Setting region override from Config: {} to {}", romHeaderRegion, regionOvrConfig);
+ return regionOvrConfig;
+ }
+ if (securityCodeRegion != null && securityCodeRegion != romHeaderRegion) {
+ LOG.info("Setting region from securityCode: {} to {}", romHeaderRegion, securityCodeRegion);
+ return securityCodeRegion;
+ }
+ if (regionFileName != null && regionFileName != romHeaderRegion) {
+ LOG.info("Setting region from fileName: {} to {}", romHeaderRegion, regionFileName);
+ return regionFileName;
+ }
+ LOG.info("Region: {}", romHeaderRegion);
+ return romHeaderRegion;
+ }
+
+ public static Region getRegionFileName(String fileName) {
+ boolean eu = fileName.toUpperCase().contains("(EUROPE)");
+ boolean us = fileName.toUpperCase().contains("(US)");
+ boolean jp = fileName.toUpperCase().contains("(JAPAN)");
+ Region r = eu ? Region.EUROPE : null;
+ r = jp ? Region.JAPAN : r;
+ r = us ? Region.USA : r;
+ return r;
}
/**
diff --git a/src/test/java/omegadrive/automated/MdRomHeaderTest.java b/src/test/java/omegadrive/automated/MdRomHeaderTest.java
index 2ad14289..724cc50b 100644
--- a/src/test/java/omegadrive/automated/MdRomHeaderTest.java
+++ b/src/test/java/omegadrive/automated/MdRomHeaderTest.java
@@ -46,7 +46,7 @@ private void testCartridgeInfo() throws Exception {
CartridgeInfoProvider cartridgeInfoProvider = MdCartInfoProvider.createInstance(memoryProvider,
rom);
System.out.println(cartridgeInfoProvider);
- System.out.println(RegionDetector.detectRegion((MdCartInfoProvider) cartridgeInfoProvider));
+ System.out.println(RegionDetector.detectHeaderRegion((MdCartInfoProvider) cartridgeInfoProvider));
} catch (Exception e) {
System.err.println("Exception: " + rom.getFileName());
e.printStackTrace();
diff --git a/src/test/java/omegadrive/cart/mapper/md/EepromTest.java b/src/test/java/omegadrive/cart/mapper/md/EepromTest.java
index 46a82fbc..4e075ca3 100644
--- a/src/test/java/omegadrive/cart/mapper/md/EepromTest.java
+++ b/src/test/java/omegadrive/cart/mapper/md/EepromTest.java
@@ -1,6 +1,5 @@
package omegadrive.cart.mapper.md;
-import omegadrive.cart.MdCartInfoProvider;
import omegadrive.cart.loader.MdLoader;
import omegadrive.cart.loader.MdRomDbModel;
import omegadrive.cart.mapper.RomMapper;
@@ -9,6 +8,7 @@
import org.junit.jupiter.api.Test;
import static omegadrive.cart.MdCartInfoProvider.DEFAULT_SRAM_START_ADDRESS;
+import static omegadrive.cart.MdCartInfoProvider.NO_PROVIDER;
import static omegadrive.cart.loader.MdRomDbModel.NO_ENTRY;
@@ -33,8 +33,7 @@ public void writeData(int address, int data, Size size) {
private void setup() {
MdRomDbModel.RomDbEntry romDbEntry = MdLoader.getEntry(nflQc32x_serial);
Assertions.assertNotEquals(NO_ENTRY, romDbEntry);
- MdCartInfoProvider provider = new MdCartInfoProvider();
- mapper = MdBackupMemoryMapper.createInstance(rom, provider, RomMapper.SramMode.READ_WRITE, romDbEntry);
+ mapper = MdBackupMemoryMapper.createInstance(rom, NO_PROVIDER, RomMapper.SramMode.READ_WRITE, romDbEntry);
}
/**