diff --git a/CHANGES.md b/CHANGES.md
index 22ad66d..7fe0f92 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,10 @@
# Release notes
+### v3.27 (released 2023-04-26)
+- Bundles GBxCart RW v1.4/v1.4a firmware version R42+L10 (improves flash cart compatibility) *(thanks wickawack)*
+- Added support for cartridges with MX29GL128EHT2I and ALTERA CPLD *(thanks Merkin)*
+- Improved writing speed for cartridges with MSP54LV512 (no PCB text) *(thanks SH for the contribution)*
+- Minor bug fixes and improvements *(thanks ide)*
+
### v3.26 (released 2023-04-18)
- Fixed a bug that made exporting Game Boy Camera pictures with a frame not work *(thanks Ell)*
- Fixed a bug with Game Boy ROM write verification on sectors smaller than 0x4000 bytes *(thanks KOOORAY)*
diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py
index 8061828..2dd44b1 100644
--- a/FlashGBX/FlashGBX_GUI.py
+++ b/FlashGBX/FlashGBX_GUI.py
@@ -815,7 +815,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
msg += message + "\n\n"
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), msg[:-2], QtWidgets.QMessageBox.Ok)
else:
- QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="No compatible devices found. Please ensure the device is connected properly.\n\nTroubleshooting advice:\n- Reconnect the device and check if the operating system detects it\n- Try different USB ports and cables, avoid passive USB hubs\n- Use a USB data cable (battery charging cables may not work)\n- Ensure your user account has permissions to use the device\n- Refer to the device compatibility list on the GitHub page".replace("\n", "
"), standardButtons=QtWidgets.QMessageBox.Ok).exec()
+ QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="No compatible devices found. Please ensure the device is connected properly.\n\nTroubleshooting advice:\n- Reconnect the device, try different USB ports/cables, avoid passive USB hubs\n- Use a USB data cable (battery charging cables may not work)\n- Check if the operating system detects the device (if not, reboot your machine)\n- Ensure your user account has permissions to use the device\n- Refer to the device compatibility list on the GitHub page".replace("\n", "
"), standardButtons=QtWidgets.QMessageBox.Ok).exec()
self.lblDevice.setText("No devices found.")
self.lblDevice.setStyleSheet("")
@@ -1047,7 +1047,24 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if "broken_sectors" in self.CONN.INFO:
s = ""
for sector in self.CONN.INFO["broken_sectors"]: s += "0x{:X}~0x{:X}, ".format(sector[0], sector[0]+sector[1]-1)
- answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "The ROM was written completely, but verification of written data failed in the following sector(s): {:s}.\n\nDo you want to try and write the sectors again that failed verification?".format(s[:-2]), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
+ msg_v = "The ROM was written completely, but verification of written data failed in the following sector(s): {:s}.".format(s[:-2])
+ if "verify_error_params" in self.CONN.INFO:
+ if self.CONN.GetMode() == "DMG":
+ cart_types = self.CONN.GetSupportedCartridgesDMG()[0]
+ elif self.CONN.GetMode() == "AGB":
+ cart_types = self.CONN.GetSupportedCartridgesAGB()[0]
+ cart_type_str = " ({:s})".format(cart_types[self.CONN.INFO["dump_info"]["cart_type"]])
+ msg_v += "\n\nTips:\n- Clean cartridge contacts\n- Check soldering if it’s a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n- Check cartridge ROM storage size (at least {:s} is required)".format(cart_type_str, Util.formatFileSize(self.CONN.INFO["verify_error_params"]["rom_size"]))
+ if "mapper_selection_type" in self.CONN.INFO["verify_error_params"]:
+ if self.CONN.INFO["verify_error_params"]["mapper_selection_type"] == 1: # manual
+ msg_v += "\n- Check mapper type used: {:s} (manual selection)".format(self.CONN.INFO["verify_error_params"]["mapper_name"])
+ elif self.CONN.INFO["verify_error_params"]["mapper_selection_type"] == 2: # forced by cart type
+ msg_v += "\n- Check mapper type used: {:s} (forced by selected cartridge type)".format(self.CONN.INFO["verify_error_params"]["mapper_name"])
+ if self.CONN.INFO["verify_error_params"]["rom_size"] > self.CONN.INFO["verify_error_params"]["mapper_max_size"]:
+ msg_v += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(self.CONN.INFO["verify_error_params"]["mapper_max_size"]))
+ msg_v += "\n\nDo you want to try and write the sectors again that failed verification?"
+
+ answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg_v, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
if answer == QtWidgets.QMessageBox.Yes:
args = self.STATUS["args"]
args.update({"flash_sectors":self.CONN.INFO["broken_sectors"]})
diff --git a/FlashGBX/Util.py b/FlashGBX/Util.py
index 52b4b7a..7b4c95b 100644
--- a/FlashGBX/Util.py
+++ b/FlashGBX/Util.py
@@ -7,9 +7,9 @@ from enum import Enum
# Common constants
APPNAME = "FlashGBX"
-VERSION_PEP440 = "3.26"
+VERSION_PEP440 = "3.27"
VERSION = "v{:s}".format(VERSION_PEP440)
-VERSION_TIMESTAMP = 1681817120
+VERSION_TIMESTAMP = 1682502626
DEBUG = False
DEBUG_LOG = []
APP_PATH = ""
diff --git a/FlashGBX/config/fc_AGB_M29W128GH.txt b/FlashGBX/config/fc_AGB_M29W128GH.txt
index d6aad0a..531c533 100644
--- a/FlashGBX/config/fc_AGB_M29W128GH.txt
+++ b/FlashGBX/config/fc_AGB_M29W128GH.txt
@@ -1,10 +1,12 @@
{
"type":"AGB",
"names":[
- "AGB-E20-30 with M29W128GH"
+ "AGB-E20-30 with M29W128GH",
+ "MX29GL128EHT2I and ALTERA CPLD"
],
"flash_ids":[
- [ 0x20, 0x00, 0x7E, 0x22 ]
+ [ 0x20, 0x00, 0x7E, 0x22 ],
+ [ 0xC2, 0x00, 0x7E, 0x22 ]
],
"voltage":3.3,
"flash_size":0x1000000,
diff --git a/FlashGBX/config/fc_DMG_MSP54LV512.txt b/FlashGBX/config/fc_DMG_MSP54LV512.txt
new file mode 100644
index 0000000..43bc834
--- /dev/null
+++ b/FlashGBX/config/fc_DMG_MSP54LV512.txt
@@ -0,0 +1,73 @@
+{
+ "type":"DMG",
+ "names":[
+ "MSP54LV512 (no PCB text)"
+ ],
+ "flash_ids":[
+ [ 0x02, 0x7D, 0x00, 0x09 ]
+ ],
+ "voltage":5,
+ "flash_size":0x2000000,
+ "start_addr":0,
+ "first_bank":1,
+ "buffer_size":32,
+ "sector_size":0x10000,
+ "reset_every":0x800000,
+ "mbc":0x201,
+ "write_pin":"WR",
+ "command_set":"AMD",
+ "commands":{
+ "reset":[
+ [ 0, 0xF0 ]
+ ],
+ "read_identifier":[
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0x90 ]
+ ],
+ "sector_erase":[
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0x80 ],
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ "SA", 0x30 ]
+ ],
+ "sector_erase_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ "SA", 0xFF, 0xFF ]
+ ],
+ "buffer_write":[
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ "SA", 0x26 ],
+ [ "SA", "BS" ],
+ [ "PA", "PD" ],
+ [ "SA", 0x2A ]
+ ],
+ "buffer_write_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ "SA", "PD", 0xFFFF ]
+ ],
+ "single_write":[
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0xA0 ],
+ [ "PA", "PD" ]
+ ],
+ "single_write_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_MSP55LV512.txt b/FlashGBX/config/fc_DMG_MSP55LV512.txt
index a403162..506ddd2 100644
--- a/FlashGBX/config/fc_DMG_MSP55LV512.txt
+++ b/FlashGBX/config/fc_DMG_MSP55LV512.txt
@@ -1,12 +1,10 @@
{
"type":"DMG",
"names":[
- "SD008-6810-512S with MSP55LV512",
- "MSP54LV512 (no PCB text)"
+ "SD008-6810-512S with MSP55LV512"
],
"flash_ids":[
- [ 0x02, 0x7D, 0x00, 0x08 ],
- [ 0x02, 0x7D, 0x00, 0x09 ]
+ [ 0x02, 0x7D, 0x00, 0x08 ]
],
"voltage":3.3,
"voltage_variants":true,
diff --git a/FlashGBX/fw_GBxCartRW_v1_3.py b/FlashGBX/fw_GBxCartRW_v1_3.py
index 41e9947..362395f 100644
--- a/FlashGBX/fw_GBxCartRW_v1_3.py
+++ b/FlashGBX/fw_GBxCartRW_v1_3.py
@@ -307,7 +307,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
while True:
try:
- dev = serial.Serial(port=port, baudrate=9600, timeout=1)
+ dev = serial.Serial(port=port, baudrate=9600*4, timeout=1)
except:
fncSetStatus(text="Status: Device access error.", enableUI=True)
return 2
diff --git a/FlashGBX/fw_GBxCartRW_v1_4.py b/FlashGBX/fw_GBxCartRW_v1_4.py
index 45ceadb..57ad10e 100644
--- a/FlashGBX/fw_GBxCartRW_v1_4.py
+++ b/FlashGBX/fw_GBxCartRW_v1_4.py
@@ -98,7 +98,7 @@ try:
QtWidgets.QDialog.__init__(self)
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
- self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW v1.4")
+ self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW")
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
self.APP = app
@@ -110,7 +110,7 @@ try:
self.DEVICE = device
else:
self.APP.QT_APP.processEvents()
- text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4 devices only. Please only proceed if your device matches this hardware revision.\n\nOlder GBxCart RW revisions can be updated only after connecting to them first."
+ text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4/v1.4a devices only. Please only proceed if your device matches this hardware revision.\n\nOlder GBxCart RW revisions can be updated only after connecting to them first."
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
answer = msgbox.exec()
diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py
index 8ade987..118ae04 100644
--- a/FlashGBX/hw_GBxCartRW.py
+++ b/FlashGBX/hw_GBxCartRW.py
@@ -16,8 +16,8 @@ from . import Util
class GbxDevice:
DEVICE_NAME = "GBxCart RW"
DEVICE_MIN_FW = 1
- DEVICE_MAX_FW = 9
- DEVICE_LATEST_FW_TS = { 4:1681739002, 5:1681395695, 6:1681395696 }
+ DEVICE_MAX_FW = 10
+ DEVICE_LATEST_FW_TS = { 4:1682502626, 5:1681900614, 6:1681900614 }
DEVICE_CMD = {
"NULL":0x30,
@@ -165,15 +165,10 @@ class GbxDevice:
if self.FW is not None:
conn_msg.append([0, "Couldn’t communicate with the GBxCart RW device on port " + ports[i] + ". Please disconnect and reconnect the device, then try again."])
continue
- elif self.FW is None or "cfw_id" not in self.FW or self.FW["cfw_id"] != 'L': # Not a CFW by Lesserkuma
+ elif self.FW is None or "cfw_id" not in self.FW or self.FW["cfw_id"] != 'L' or self.FW["fw_ver"] < self.DEVICE_MIN_FW or (self.FW["pcb_ver"] < 5 and self.FW["fw_ver"] != 1): # Not a CFW by Lesserkuma
dev.close()
self.DEVICE = None
continue
- elif self.FW["fw_ver"] < self.DEVICE_MIN_FW:
- dev.close()
- self.DEVICE = None
- conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " requires a firmware update to work with this software. Please try again after updating it to version L" + str(self.DEVICE_MIN_FW) + " or higher.
Firmware updates are available at https://www.gbxcart.com/."])
- continue
elif self.FW["fw_ts"] > self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
conn_msg.append([0, "Note: The GBxCart RW device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
@@ -375,8 +370,10 @@ class GbxDevice:
def FirmwareUpdateAvailable(self):
if self.FW["pcb_ver"] not in (4, 5, 6): return False
- if (self.FW["pcb_ver"] in (4, 5, 6) and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]):
- if self.FW["pcb_ver"] == 4: self.FW_UPDATE_REQ = True
+ if self.FW["pcb_ver"] in (5, 6) and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
+ return True
+ if self.FW["pcb_ver"] == 4 and self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
+ self.FW_UPDATE_REQ = True
return True
def GetFirmwareUpdaterClass(self):
@@ -2181,7 +2178,7 @@ class GbxDevice:
if (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY"):
temp = self.ReadROM_3DMemory(address=pos, length=buffer_len, max_length=max_length)
else:
- if self.FW["fw_ver"] >= 9 and "verify_write" in args and (self.MODE != "AGB" or args["verify_base_pos"] > 0xC9):
+ if self.FW["fw_ver"] >= 10 and "verify_write" in args and (self.MODE != "AGB" or args["verify_base_pos"] > 0xC9):
# Verify mode (by CRC32)
dprint("CRC32 verification (verify_base_pos=0x{:X}, pos=0x{:X}, pos_total=0x{:X}, buffer_len=0x{:X})".format(args["verify_base_pos"], pos, pos_total, buffer_len))
if self.MODE == "DMG":
@@ -2202,7 +2199,7 @@ class GbxDevice:
continue
else:
dprint("Mismatch during CRC32 verification between 0x{:X} and 0x{:X}".format(pos_total, pos_total+buffer_len))
- return pos_total
+ temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
else:
# Normal read
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
@@ -3556,6 +3553,15 @@ class GbxDevice:
if len(broken_sectors) > 0:
self.INFO["broken_sectors"] = broken_sectors
+ self.INFO["verify_error_params"] = {}
+ self.INFO["verify_error_params"]["rom_size"] = len(data_import)
+ if self.MODE == "DMG":
+ self.INFO["verify_error_params"]["mapper_name"] = _mbc.GetName()
+ if flashcart.GetMBC() == "manual":
+ self.INFO["verify_error_params"]["mapper_selection_type"] = 1 # manual
+ else:
+ self.INFO["verify_error_params"]["mapper_selection_type"] = 2 # forced by cart type
+ self.INFO["verify_error_params"]["mapper_max_size"] = _mbc.GetMaxROMSize()
verified = False
# ↑↑↑ Flash verify
diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip
index 43f18ab..e458d3a 100644
Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_3.zip b/FlashGBX/res/fw_GBxCart_RW_v1_3.zip
index fd3d2d3..0f5b15b 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_3.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_3.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip
index 98fd157..38631b0 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip
index 3267475..bbf6fb4 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip differ
diff --git a/README.md b/README.md
index 7b987bd..9ef8a52 100644
--- a/README.md
+++ b/README.md
@@ -265,6 +265,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- M5M29G130AN (no PCB text)
- M6MGJ927 (no PCB text)
- MSP54LV512 (no PCB text)
+ - MX29GL128EHT2I and ALTERA CPLD
- SUN100S_MSP54XXX with MSP54LV100
Many different reproduction cartridges share their flash chip command set, so even if yours is not on this list, it may still work fine or even be auto-detected as another one. Support for more cartridges can also be added by creating external config files that include the necessary flash chip commands.
@@ -301,7 +302,7 @@ Many different reproduction cartridges share their flash chip command set, so ev
The author would like to thank the following very kind people for their help, contributions or documentation (in alphabetical order):
-2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, metroid-maniac, Mr_V, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, Shinichi999, sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, Timville, twitnic, velipso, Veund, voltagex, Voultar, Wkr, x7l7j8cc, xactoes, Zeii, Zelante, zvxr
+2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, Zeii, Zelante, zvxr
## DISCLAIMER
diff --git a/setup.py b/setup.py
index df0a63d..2e4bf5c 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
setuptools.setup(
name="FlashGBX",
- version="3.26",
+ version="3.27",
author="Lesserkuma",
description="Reads and writes Game Boy and Game Boy Advance cartridge data using the GBxCart RW by insideGadgets",
url="https://github.com/lesserkuma/FlashGBX",