From 3c56ec50785bd1e1fbaf3c2f3c2f3d3c3f052738 Mon Sep 17 00:00:00 2001 From: nopjne Date: Mon, 24 Jul 2023 08:36:45 -0700 Subject: [PATCH] Cart tester and refactor --- README.rst | 220 +++++------------- examples/device/cdc_msc/CMakeLists.txt | 2 +- .../device/cdc_msc/src/generated/joybus.pio.h | 4 +- examples/device/cdc_msc/src/joybus.c | 88 ++++--- examples/device/cdc_msc/src/joybus.h | 3 +- examples/device/cdc_msc/src/joybus.pio | 4 +- .../device/cdc_msc/src/n64cartinterface.c | 203 +++++++++++++--- .../device/cdc_msc/src/n64cartinterface.h | 14 +- .../cdc_msc/src/{msc_disk.c => virtualdisk.c} | 82 ++++++- 9 files changed, 377 insertions(+), 243 deletions(-) rename examples/device/cdc_msc/src/{msc_disk.c => virtualdisk.c} (84%) diff --git a/README.rst b/README.rst index 78e8d87b0..52442bdc6 100644 --- a/README.rst +++ b/README.rst @@ -1,162 +1,60 @@ -.. figure:: docs/assets/logo.svg - :alt: TinyUSB -|Build Status| |Documentation Status| |Fuzzing Status| |License| - -TinyUSB is an open-source cross-platform USB Host/Device stack for -embedded system, designed to be memory-safe with no dynamic allocation -and thread-safe with all interrupt events are deferred then handled in -the non-ISR task function. - -Please take a look at the online `documentation `__. - -.. figure:: docs/assets/stack.svg - :width: 500px - :alt: stackup - -:: - - . - ├── docs # Documentation - ├── examples # Sample with Makefile build support - ├── hw - │ ├── bsp # Supported boards source files - │ └── mcu # Low level mcu core & peripheral drivers - ├── lib # Sources from 3rd party such as freeRTOS, fatfs ... - ├── src # All sources files for TinyUSB stack itself. - ├── test # Unit tests for the stack - └── tools # Files used internally - -Supported MCUs -============== - -The stack supports the following MCUs: - -- **Allwinner:** F1C100s/F1C200s -- **Broadcom:** BCM2837, BCM2711 -- **Dialog:** DA1469x -- **Espressif:** ESP32-S2, ESP32-S3 -- **GigaDevice:** GD32VF103 -- **Infineon:** XMC4500 -- **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55, SAML21, SAML22, SAME7x -- **NordicSemi:** nRF52833, nRF52840, nRF5340 -- **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505 -- **NXP:** - - - iMX RT Series: RT10xx, RT11xx - - Kinetis: KL25, K32L2 - - LPC Series: 11u, 13, 15, 17, 18, 40, 43, 51u, 54, 55 - -- **Raspberry Pi:** RP2040 -- **Renesas:** - - - RX Series: 63N, 65N, 72N - - RA Series: RA4M1, RA4M3 - -- **Silabs:** EFM32GG -- **Sony:** CXD56 -- **ST:** STM32 series: F0, F1, F2, F3, F4, F7, H7, G0, G4, L0, L1, L4, L4+, WB -- **TI:** MSP430, MSP432E4, TM4C123 -- **ValentyUSB:** eptri -- **WCH:** CH32V307 - -Here is the list of `Supported Devices`_ that can be used with provided examples. - -Device Stack -============ - -Supports multiple device configurations by dynamically changing USB descriptors, low power functions such like suspend, resume, and remote wakeup. The following device classes are supported: - -- Audio Class 2.0 (UAC2) -- Bluetooth Host Controller Interface (BTH HCI) -- Communication Device Class (CDC) -- Device Firmware Update (DFU): DFU mode (WIP) and Runtime -- Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ... -- Mass Storage Class (MSC): with multiple LUNs -- Musical Instrument Digital Interface (MIDI) -- Network with RNDIS, Ethernet Control Model (ECM), Network Control Model (NCM) -- Test and Measurement Class (USBTMC) -- Video class 1.5 (UVC): work in progress -- Vendor-specific class support with generic In & Out endpoints. Can be used with MS OS 2.0 compatible descriptor to load winUSB driver without INF file. -- `WebUSB `__ with vendor-specific class - -If you have a special requirement, `usbd_app_driver_get_cb()` can be used to write your own class driver without modifying the stack. Here is how the RPi team added their reset interface `raspberrypi/pico-sdk#197 `_ - -Host Stack -========== - -- Human Interface Device (HID): Keyboard, Mouse, Generic -- Mass Storage Class (MSC) -- Hub with multiple-level support - -TypeC PD Stack -============== - -- Power Delivery 3.0 (PD3.0) with USB Type-C support (WIP) -- Super early stage, only for testing purpose -- Only support STM32 G4 - -OS Abstraction layer -==================== - -TinyUSB is completely thread-safe by pushing all Interrupt Service Request (ISR) events into a central queue, then processing them later in the non-ISR context task function. It also uses semaphore/mutex to access shared resources such as Communication Device Class (CDC) FIFO. Therefore the stack needs to use some of the OS's basic APIs. Following OSes are already supported out of the box. - -- **No OS** -- **FreeRTOS** -- `RT-Thread `_: `repo `_ -- **Mynewt** Due to the newt package build system, Mynewt examples are better to be on its `own repo `_ - -Docs -==== - -- Info - - - `Uses`_ - - `Changelog`_ - - `Contributors`_ - -- `Reference`_ - - - `Supported Devices`_ - - `Getting Started`_ - - `Dependencies`_ - - `Concurrency`_ - -- `Contributing`_ - - - `Code of Conduct`_ - - `Structure`_ - - `Porting`_ - -License -======= - -All TinyUSB sources in the ``src`` folder are licensed under MIT -license, the `Full license is here `__. However, each file can be -individually licensed especially those in ``lib`` and ``hw/mcu`` folder. -Please make sure you understand all the license term for files you use -in your project. - - -.. |Build Status| image:: https://github.com/hathach/tinyusb/actions/workflows/cmake_arm.yml/badge.svg - :target: https://github.com/hathach/tinyusb/actions -.. |Documentation Status| image:: https://readthedocs.org/projects/tinyusb/badge/?version=latest - :target: https://docs.tinyusb.org/en/latest/?badge=latest -.. |Fuzzing Status| image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/tinyusb.svg - :target: https://oss-fuzz-build-logs.storage.googleapis.com/index.html#tinyusb -.. |License| image:: https://img.shields.io/badge/license-MIT-brightgreen.svg - :target: https://opensource.org/licenses/MIT - - -.. _Uses: docs/info/uses.rst -.. _Changelog: docs/info/changelog.rst -.. _Contributors: CONTRIBUTORS.rst -.. _Reference: docs/reference/index.rst -.. _Supported Devices: docs/reference/supported.rst -.. _Getting Started: docs/reference/getting_started.rst -.. _Dependencies: docs/reference/dependencies.rst -.. _Concurrency: docs/reference/concurrency.rst -.. _Contributing: docs/contributing/index.rst -.. _Code of Conduct: CODE_OF_CONDUCT.rst -.. _Structure: docs/contributing/structure.rst -.. _Porting: docs/contributing/porting.rst +DrmDmp64_mass is a mass storage device firmware for the DreamDumper64 project. +The DreamDumper64 uses a WeAct pico to read N64 caridges. +The DrmDump64_mass firmware is able to read up to 64MB, ROMs it also does its best to detect the actual rom size. +It also reads the save game chips, such as the SRAM (battery backed), SI EEPROM (most common) and FlashRAM (largests size). +All of these chips are exposed to the host PC as a file on a USB drive. + +The DrmDmp64_mass also does cartridge verification, it will detect whether subsystems of the cartridge are present and/or working. \ +``` +"Cart tester report:" +" EEPROM - Not present" +" SRAM - OK!" +" FlashRam - OK!" +" CIC - PAL 6103" +" Romsize - 16MB" +" RomName - POKEMON SNAP" +" RomID - 504E PF" +" CartType - N" +" RomRegion - F" +" RomVersion - 00" +``` + +The following files are exposed from the virtual disk: \ +``` +D:\>dir /s + Volume in drive D is DreamDump64 + Volume Serial Number is 0022-DC8F + + Directory of D:\ + +09/05/2008 04:20 PM 512 ROM.EEP +09/05/2008 04:20 PM 131,072 ROM.FLA +09/05/2008 04:20 PM 12,582,912 ROM.N64 +09/05/2008 04:20 PM 12,582,912 ROMF.Z64 +09/05/2008 04:20 PM 131,072 ROMF.RAM +09/05/2008 04:20 PM 512 ROMF.EEP +09/05/2008 04:20 PM 2,048 CARTTEST.TXT + 7 File(s) 25,431,040 bytes + +ROM.EEP - Is either 512Byte or 2048Byte depending on 4K or 16K eeprom. +ROM.FLA - Is either the SRAM or FlashRAM, which is between 32KB or 128KB, the file is always exposed as 128KB for compatibility with the DaisyDrive64. +ROM.N64 - Is the N64 Native format of the ROM, this format is directly compatible with the DaisyDrive64. +ROMF.Z64 - This is the same data as ROM.N64 however 16bit byte flipped, for compatibility with PC emulators. +ROMF.RAM - Is the SRAM or FlashRAM data in byteflipped mode, for compatibility with PC emulators. +ROMF.EEP - Is the EEPROM data in byteflipped mode, for compatibility with PC emulators. +CARTTEST.TXT - Is the output of the cart tester during initialization. +``` +How to use: \ +``` +1. Insert cartridge into the cartridge slot. +2. Connect USB to PC. +3. Navigate to the drive, use normally. +``` +NOTE: When swapping cartridges make sure you disconnect and eject the drive, otherwise the operating system may cache the files from the previous cartridge. + +Please look for PCBs here: +https://dreamcraftindustries.com/products/dreamdump64-pcb + +Gerber files here: +https://github.com/khill25/Dreamdumper \ No newline at end of file diff --git a/examples/device/cdc_msc/CMakeLists.txt b/examples/device/cdc_msc/CMakeLists.txt index 5b029fa73..c4aa1f6fe 100644 --- a/examples/device/cdc_msc/CMakeLists.txt +++ b/examples/device/cdc_msc/CMakeLists.txt @@ -31,7 +31,7 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_LIST_DIR}/src/generated/joybus.pio.h target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/virtualdisk.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ${CMAKE_CURRENT_SOURCE_DIR}/src/n64cartinterface.c ${CMAKE_CURRENT_SOURCE_DIR}/src/joybus.c diff --git a/examples/device/cdc_msc/src/generated/joybus.pio.h b/examples/device/cdc_msc/src/generated/joybus.pio.h index 329525ba6..c03ead26c 100644 --- a/examples/device/cdc_msc/src/generated/joybus.pio.h +++ b/examples/device/cdc_msc/src/generated/joybus.pio.h @@ -39,8 +39,8 @@ static const uint16_t joybus_program_instructions[] = { 0xf301, // 15: set pins, 1 [19] 0x0008, // 16: jmp 8 0xe081, // 17: set pindirs, 1 - 0xe601, // 18: set pins, 1 [6] - 0xe600, // 19: set pins, 0 [6] + 0xe501, // 18: set pins, 1 [5] + 0xe500, // 19: set pins, 0 [5] 0x0012, // 20: jmp 18 // .wrap }; diff --git a/examples/device/cdc_msc/src/joybus.c b/examples/device/cdc_msc/src/joybus.c index 3128b9b19..759529b00 100644 --- a/examples/device/cdc_msc/src/joybus.c +++ b/examples/device/cdc_msc/src/joybus.c @@ -52,6 +52,8 @@ void __time_critical_func(InitEepromClock)(uint clockpin) gpio_init(clockpin); gpio_set_dir(clockpin, GPIO_OUT); + pio_gpio_init(pio_1, clockpin); + uint offset_1 = pio_add_program(pio_1, &joybus_program); pio_sm_config config1 = joybus_program_get_default_config(offset_1); //sm_config_set_out_pins(&config1, clockpin, 1); @@ -64,19 +66,38 @@ void __time_critical_func(InitEepromClock)(uint clockpin) pio_sm_set_enabled(pio_1, 1, true); } +uint32_t GetInputWithTimeout(void) +{ + uint32_t lastWriteTime = time_us_32(); + while (1) { + if(pio_sm_is_rx_fifo_empty(pio, 0)) { + uint32_t now = time_us_32(); + uint32_t diff = now - lastWriteTime; + + // Send the eeprom data if it's been ?Seconds since the last eeprom write + // Reset the lastWriteTime to 0 and don't sent the data unless we get another write + if (lastWriteTime != 0 && diff > 100000) { + lastWriteTime = 0; + break; + } + } else { + return pio_sm_get(pio, 0); + } + } + + return 0xFFFFFFFF; +} + pio_sm_config config; -void __time_critical_func(InitEeprom)(uint dataPin, uint clockpin) +void __time_critical_func(InitEeprom)(uint dataPin) { gpio_init(dataPin); gpio_set_dir(dataPin, GPIO_IN); gpio_pull_up(dataPin); - InitEepromClock(clockpin); - sleep_us(100); // Stabilize voltages pio_gpio_init(pio, dataPin); - pio_gpio_init(pio_1, clockpin); uint offset = pio_add_program(pio, &joybus_program); config = joybus_program_get_default_config(offset); @@ -86,20 +107,20 @@ void __time_critical_func(InitEeprom)(uint dataPin, uint clockpin) sm_config_set_clkdiv(&config, 5); sm_config_set_out_shift(&config, true, false, 32); sm_config_set_in_shift(&config, false, true, 8); - + pio_sm_init(pio, 0, offset, &config); pio_sm_set_enabled(pio, 0, true); - + // Send the info command { uint8_t probeResponse[1] = {0x00}; - uint32_t result[2]; + uint32_t result[8]; int resultLen; convertToPio(probeResponse, 1, result, &resultLen); sleep_us(6); // 3.75us into the bit before end bit => 6.25 to wait if the end-bit is 5us long pio_sm_set_enabled(pio, 0, false); - pio_sm_init(pio, 0, offset+joybus_offset_outmode, &config); + pio_sm_init(pio, 0, offset + joybus_offset_outmode, &config); pio_sm_set_enabled(pio, 0, true); for (int i = 0; i < resultLen; i++) pio_sm_put_blocking(pio, 0, result[i]); @@ -107,26 +128,25 @@ void __time_critical_func(InitEeprom)(uint dataPin, uint clockpin) // Check response uint32_t buffer[3]; - sleep_us(35); - pio_sm_set_enabled(pio, 0, false); - pio_sm_init(pio, 0, offset + joybus_offset_inmode, &config); - pio_sm_set_enabled(pio, 0, true); - buffer[0] = pio_sm_get(pio, 0); - //buffer[0] = pio_sm_get_blocking(pio, 0); - - // Determine the size of the EEPROM. - if (buffer[0] == 0x80) { - // 4K Eeprom. - ReadCount = 64; - gEepromSize = 512; - } else if (buffer[0] == 0xC0) { - // 16K Eeprom. - ReadCount = 256; - gEepromSize = 512 * 4; - } else { - // Unknown SI eeprom type. - ReadCount = 0; - gEepromSize = 0; + buffer[0] = GetInputWithTimeout(); + if (buffer[0] == 0) { + buffer[1] = pio_sm_get_blocking(pio, 0); + buffer[2] = pio_sm_get_blocking(pio, 0); + + // Determine the size of the EEPROM. + if (buffer[1] == 0x80) { + // 4K Eeprom. + ReadCount = 64; + gEepromSize = 512; + } else if (buffer[1] == 0xC0) { + // 16K Eeprom. + ReadCount = 256; + gEepromSize = 512 * 4; + } else { + // Unknown SI eeprom type. + ReadCount = 0; + gEepromSize = 0; + } } } @@ -140,7 +160,7 @@ void __time_critical_func(ReadEepromData)(uint32_t offset, uint8_t *buffer) for (uint32_t ReadIndex = 0; ReadIndex < 64; ReadIndex += 1) { // Construct the read command. uint8_t probeResponse[] = {0x04, (uint8_t)(ReadIndex + offset)}; - uint32_t result[3]; + uint32_t result[8]; int resultLen; convertToPio(probeResponse, 1, result, &resultLen); sleep_us(6); // 3.75us into the bit before end bit => 6.25 to wait if the end-bit is 5us long @@ -153,11 +173,8 @@ void __time_critical_func(ReadEepromData)(uint32_t offset, uint8_t *buffer) for (int i = 0; i < resultLen; i++) pio_sm_put_blocking(pio, 0, result[i]); // Read the incoming data from the cart. - pio_sm_set_enabled(pio, 0, false); - pio_sm_init(pio, 0, offset + joybus_offset_inmode, &config); - pio_sm_set_enabled(pio, 0, true); - for (uint i = 0; i < 8; i += 1) { - buffer[i + ReadIndex] = (uint8_t)pio_sm_get_blocking(pio, 0); + for (int i = 0; i < resultLen; i += 1) { + buffer[(uint)i + (uint)ReadIndex] = (uint8_t)pio_sm_get_blocking(pio, 0); } } } @@ -186,9 +203,6 @@ void __time_critical_func(WriteEepromData)(uint32_t offset, uint8_t *buffer) // Read the incoming data from the cart. uint8_t response[2]; - pio_sm_set_enabled(pio, 0, false); - pio_sm_init(pio, 0, offset + joybus_offset_inmode, &config); - pio_sm_set_enabled(pio, 0, true); for (int i = 0; i < 2; i += 1) { response[i] = (uint8_t)pio_sm_get_blocking(pio, 0); } diff --git a/examples/device/cdc_msc/src/joybus.h b/examples/device/cdc_msc/src/joybus.h index ad7b7f4c4..124d15eb5 100644 --- a/examples/device/cdc_msc/src/joybus.h +++ b/examples/device/cdc_msc/src/joybus.h @@ -7,7 +7,8 @@ */ #pragma once -void InitEeprom(uint dataPin, uint clockpin); +void InitEeprom(uint dataPin); +void InitEepromClock(uint clockpin); void ReadEepromData(uint32_t offset, uint8_t *buffer); void WriteEepromData(uint32_t offset, uint8_t *buffer); diff --git a/examples/device/cdc_msc/src/joybus.pio b/examples/device/cdc_msc/src/joybus.pio index d074d3482..24d1f67d0 100644 --- a/examples/device/cdc_msc/src/joybus.pio +++ b/examples/device/cdc_msc/src/joybus.pio @@ -32,7 +32,7 @@ outagain: ; PUBLIC clockgen: set pindirs, 1 ; clockstart: - set pins, 1 [6] - set pins, 0 [6] + set pins, 1 [5] + set pins, 0 [5] jmp clockstart ; \ No newline at end of file diff --git a/examples/device/cdc_msc/src/n64cartinterface.c b/examples/device/cdc_msc/src/n64cartinterface.c index 9ea44e36a..bc2496af7 100644 --- a/examples/device/cdc_msc/src/n64cartinterface.c +++ b/examples/device/cdc_msc/src/n64cartinterface.c @@ -23,10 +23,29 @@ #define SRAM_ADDRESS_START (0x08000000) uint32_t readarr[32768]; +#define CRC_NUS_5101 0x587BD543 // ?? +#define CRC_NUS_6101 0x9AF30466 //0x6170A4A1 +#define CRC_NUS_7102 0x009E9EA3 // ?? +#define CRC_NUS_6102 0x6D089C64 //0x90BB6CB5 +#define CRC_NUS_6103 0x211BA9FB //0x0B050EE0 +#define CRC_NUS_6105 0x520D9ABB //0x98BC2C86 +#define CRC_NUS_6106 0x266C376C //0xACC8580A +#define CRC_NUS_8303 0x0E018159 // ?? +#define CRC_iQue_1 0xCD19FEF1 +#define CRC_iQue_2 0xB98CED9A +#define CRC_iQue_3 0xE71C2766 +#define CRC_NUS_7101 0x12706049 + uint32_t address_pin_mask = 0; static char gpio_is_output = 0; uint32_t gRomSize = 64 * 1024 * 1024; uint32_t gFramPresent = 0; +uint32_t gSRAMPresent = 1; +uint32_t gCICType = 0xFF; +uint16_t gGameTitle[0x16]; +uint16_t gGameCode[6]; +uint32_t gChecksum; +const char* gCICName; void set_ad_input() { for(uint32_t i = 0; i < 16; i++) { @@ -46,6 +65,39 @@ void set_ad_output() { gpio_is_output = 1; } +uint32_t CrcTable[256]; +bool TableBuilt = false; +uint32_t si_crc32(const uint8_t *data, size_t size) { + unsigned n, k; + uint32_t c; + + // No need to recompute the table on every invocation. + if (TableBuilt == false) { + for (n = 0; n < 256; n++) { + c = (uint32_t) n; + for (k = 0; k < 8; k++) { + if (c & 1) { + c = 0xEDB88320L ^ (c >> 1); + + } else { + c = c >> 1; + } + + CrcTable[n] = c; + } + } + + TableBuilt = true; + } + + c = 0L ^ 0xFFFFFFFF; + for (n = 0; n < size; n++) { + c = CrcTable[(c ^ data[n]) & 0xFF] ^ (c >> 8); + } + + return c ^ 0xFFFFFFFF; +} + void cartio_init() { // Setup the LED pin @@ -58,12 +110,6 @@ void cartio_init() gpio_set_dir(N64_COLD_RESET, true); gpio_put(N64_COLD_RESET, false); - // Prepare the S-DAT pin. - //gpio_init(N64_EEPROM_DAT); - //gpio_set_dir(N64_EEPROM_DAT, false); - //gpio_put(N64_EEPROM_DAT, true); - //gpio_set_pulls(N64_EEPROM_DAT, true, false); - // Indicate the initialization process through the onboard led. volatile int t = 0; while(t < 5) { @@ -109,6 +155,9 @@ void cartio_init() address_pin_mask = 0xFFFF; set_ad_output(); + // Eeprom init. + InitEepromClock(N64_EEPROM_CLK); + sleep_ms(300); gpio_put(N64_COLD_RESET, true); sleep_ms(100); @@ -118,7 +167,11 @@ void cartio_init() gpio_put(N64_CIC_DCLK, true); gpio_set_pulls(N64_CIC_DCLK, true, false); - // Self test, read start address, assert that the retured value is something valid. + gpio_init(N64_CIC_DIO); + gpio_set_dir(N64_CIC_DIO, false); + gpio_set_pulls(N64_CIC_DIO, true, false); + + // Read start address, assert that the retured value is something valid. set_address(CART_ADDRESS_START); uint32_t read = (((uint32_t)read16()) << 16) | (read16()); assert(read == 0x80371240); @@ -140,25 +193,22 @@ void cartio_init() } // Check for an open bus. This means no hw is responding to the set address. -#if 0 - uint32_t OpenBusValue = (uint16_t)(x * 1024 * 1024); - OpenBusValue = OpenBusValue || (OpenBusValue << 16); - if (readcheck == OpenBusValue) { - bool IsOpenBus = true; - for (uint32_t y = 1; y < 256; y += 1) { - readcheck = (((uint32_t)read16()) << 16) | (read16()); - if (readcheck != OpenBusValue) { - IsOpenBus = false; - break; - } - } - - if (IsOpenBus != false) { - gRomSize = x * 1024 * 1024; + bool IsOpenBus = true; + for (uint32_t y = 0; y < 256; y += 1) { + set_address(CART_ADDRESS_START + (x * 1024 * 1024) + (y * 2)); + uint32_t OpenBusValue = (uint16_t)(x * 1024 * 1024) + (y * 2); + OpenBusValue = OpenBusValue | (OpenBusValue << 16); + readcheck = (((uint32_t)read16()) << 16) | (read16()); + if (readcheck != OpenBusValue) { + IsOpenBus = false; break; } } -#endif + + if (IsOpenBus != false) { + gRomSize = x * 1024 * 1024; + break; + } } // Check for FRAM presence. This write is okay on every cart @@ -186,16 +236,111 @@ void cartio_init() } - (void)readarr; - set_address(SRAM_ADDRESS_START); - for (uint i = 0; i < (sizeof(readarr) / 4); i += 1) { - readarr[i] = (((uint32_t)read16()) << 16) | (read16()); + // Check for an open bus. This means no hw is responding to the set address. + bool IsOpenBus = true; + for (uint32_t i = 0; i < 256; i += 1) { + set_address(SRAM_ADDRESS_START + (i * 2)); + uint32_t OpenBusValue = (uint16_t)(SRAM_ADDRESS_START + (i * 2)); + OpenBusValue = OpenBusValue | (OpenBusValue << 16); + uint32_t readcheck = (((uint32_t)read16()) << 16) | (read16()); + if (readcheck != OpenBusValue) { + IsOpenBus = false; + break; + } } - // Attempt init of eeprom. - InitEeprom(N64_EEPROM_DAT, N64_EEPROM_CLK); + if (IsOpenBus != false) { + gSRAMPresent = false; + set_address(SRAM_ADDRESS_START); + for (uint i = 0; i < (sizeof(readarr) / 4); i += 1) { + readarr[i] = (((uint32_t)read16()) << 16) | (read16()); + } + } + + // EEPROM init. + InitEeprom(N64_EEPROM_DAT); uint8_t Buffer[512]; ReadEepromData(0, Buffer); + + // Do cart test and get cart data. Start with the CIC hello protocol. + uint8_t CICHello = 0; + for (uint32_t x = 0; x < 4; x += 1) { + gpio_put(N64_CIC_DCLK, false); + sleep_us(10); + CICHello |= (uint8_t)(((gpio_get(N64_CIC_DIO) == false) ? 0 : 1) << (3 - x)); + sleep_us(16); + gpio_put(N64_CIC_DCLK, true); + sleep_us(20); + } + + if (CICHello == 0x5) { + gCICType = CIC_TYPE_PAL; + } else if (CICHello == 0x1) { + gCICType = CIC_TYPE_NTSC; + } else { + gCICType = CIC_TYPE_INVALID; + } + + // Read the 0x1000 bytes to determine Rom name, Cart Id, Region and CIC hash. + set_address(CART_ADDRESS_START + 0x20); + for (uint i = 0; i < (sizeof(gGameTitle) / 2); i += 1) { + gGameTitle[i] = flip16(read16()); + } + + set_address(CART_ADDRESS_START + 0x3A); + for (uint i = 0; i < (sizeof(gGameCode) / 2); i += 1) { + gGameCode[i] = read16(); + } + + uint16_t buffer[0xFC0 / 2]; + for (uint i = 0; i < (0xFC0 / 2); i += 1) { + set_address(CART_ADDRESS_START + 0x40 + (i * 2)); + buffer[i] = read16(); + } + + uint32_t crc = si_crc32((uint8_t*)buffer, sizeof(buffer)); + switch (crc) { + case CRC_NUS_6101: + gCICName = "6101"; + break; + case CRC_iQue_1: + gCICName = "iQue 1"; + break; + case CRC_iQue_2: + gCICName = "iQue 2"; + break; + case CRC_iQue_3: + gCICName = "iQue 3"; + break; + + case CRC_NUS_6102: + gCICName = "6102"; + break; + + case CRC_NUS_6103: + gCICName = "6103"; + break; + + case CRC_NUS_6105: + gCICName = "6105"; + break; + + case CRC_NUS_6106: + gCICName = "6105"; + break; + + case CRC_NUS_8303: + gCICName = "8303"; + break; + + case CRC_NUS_7101: + gCICName = "7101"; + break; + + default: + gCICName = "Unknown"; + } + } void set_address(uint32_t address) { diff --git a/examples/device/cdc_msc/src/n64cartinterface.h b/examples/device/cdc_msc/src/n64cartinterface.h index 6f5c8ba8c..3adad74ae 100644 --- a/examples/device/cdc_msc/src/n64cartinterface.h +++ b/examples/device/cdc_msc/src/n64cartinterface.h @@ -22,6 +22,12 @@ #define N64_ALEL (27) #define N64_ALEH (28) +enum CIC_TYPES { + CIC_TYPE_PAL = 0, + CIC_TYPE_NTSC = 1, + CIC_TYPE_INVALID = 0xFF, +}; + void cartio_init(void); void set_address(uint32_t address); uint16_t read16(); @@ -31,8 +37,14 @@ void write16(uint16_t value); extern uint32_t gRomSize; extern uint32_t readarr[32768]; extern uint32_t gFramPresent; +extern uint32_t gSRAMPresent; +extern uint32_t gCICType; +extern uint16_t gGameTitle[0x16]; +extern uint16_t gGameCode[6]; +extern uint32_t gChecksum; +extern const char* gCICName; -inline uint16_t flip8(uint16_t value) +inline uint16_t flip16(uint16_t value) { return (uint16_t)((uint16_t)(value) << 8) | (((uint16_t)value) >> 8); } diff --git a/examples/device/cdc_msc/src/msc_disk.c b/examples/device/cdc_msc/src/virtualdisk.c similarity index 84% rename from examples/device/cdc_msc/src/msc_disk.c rename to examples/device/cdc_msc/src/virtualdisk.c index 8310181f9..c19754c20 100644 --- a/examples/device/cdc_msc/src/msc_disk.c +++ b/examples/device/cdc_msc/src/virtualdisk.c @@ -26,7 +26,15 @@ static bool ejected = false; #define SECTOR_COUNT (VOLUME_SIZE / SECTOR_SIZE) uint8_t CartTestText[2 * 1024] = -"\nPlaceholder\n\n EEPROM - Not present\n SRAM - OK\n FlashRam - Not present\n CIC - PAL 6102\n Romsize - 16MB\n RomName - Placeholder\n"; +"\nCart tester report:\n\n" +" EEPROM - Not present\n" +" SRAM - Not present\n" +" FlashRam - Not present\n" +" CIC - PAL 6102\n" +" Romsize - 16MB\n" +" RomName - Placeholder\n" +" RomID - 00000000\n" +" RomRegion - Europe\n"; #define vd_sector_count() SECTOR_COUNT enum @@ -396,37 +404,44 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf, // we don't support that many directory entries actually if (!lba) { memset(buf, 0, buf_size); - // root directory + // root directory -- Do not use lower case letters, windows will show the file but it won't be able to "find" the data for the file. struct dir_entry *entries = (struct dir_entry *) buf; memcpy(entries[0].name, (boot_sector + BOOT_OFFSET_LABEL), 11); entries[0].attr = ATTR_VOLUME_LABEL | ATTR_ARCHIVE; uint32_t cluster_offset = 2; uint32_t size = 2 * 1024; + assert(cluster_offset == (EEPROM_CLUSTER_START + 2)); init_dir_entry(++entries, "ROM EEP", cluster_offset, gEepromSize); cluster_offset += (size / CLUSTER_SIZE) + 1; + assert(cluster_offset == (FLASHRAM_CLUSTER_START + 2)); size = 128 * 1024; init_dir_entry(++entries, "ROM FLA", cluster_offset, size); cluster_offset += size / CLUSTER_SIZE; size = (64 * 1024 * 1024); + assert(cluster_offset == (N64ROM_CLUSTER_START + 2)); init_dir_entry(++entries, "ROM N64", cluster_offset, gRomSize); cluster_offset += size / CLUSTER_SIZE; size = (64 * 1024 * 1024); + assert(cluster_offset == (Z64ROM_CLUSTER_START + 2)); init_dir_entry(++entries, "ROMF Z64", cluster_offset, gRomSize); // Same as N64 just byteflipped. cluster_offset += size / CLUSTER_SIZE; size = 128 * 1024; - init_dir_entry(++entries, "ROMF ram", cluster_offset, size); // Same as N64 just byteflipped. + assert(cluster_offset == (FLASHRAMFLIP_CLUSTER_START + 2)); + init_dir_entry(++entries, "ROMF RAM", cluster_offset, size); // Same as N64 just byteflipped. cluster_offset += size / CLUSTER_SIZE; size = 2 * 1024; + assert(cluster_offset == (EEPROMFLIP_CLUSTER_START + 2)); init_dir_entry(++entries, "ROMF EEP", cluster_offset, gEepromSize); cluster_offset += (size / CLUSTER_SIZE) + 1; size = 2 * 1024; - init_dir_entry(++entries, "CartTesttxt", cluster_offset, size); + assert(cluster_offset == (CARTTEST_CLUSTER_START + 2)); + init_dir_entry(++entries, "CARTTESTTXT", cluster_offset, size); written = 512; } else { memset(buf, 0, buf_size); @@ -439,13 +454,62 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf, written = 512; // Lookup cluster by entry if (cluster == CARTTEST_CLUSTER_START) { - uint32_t address = (((uint32_t)cluster - (CARTTEST_CLUSTER_START)) * CLUSTER_SIZE) + (cluster_offset * SECTOR_SIZE); - memcpy(buf, CartTestText + address, SECTOR_SIZE); + memset(buf, 0, SECTOR_SIZE); + if (cluster_offset == 0) { + const char* NotPresent = "Not present"; + const char* Failed = "Failed"; + const char* OK = "OK!"; + const char* PAL = "PAL"; + const char* NTSC = "NTSC"; + const char* EepString = Failed; + const char* CICString = Failed; + if (gEepromSize == 0) { + EepString = NotPresent; + } else if (gEepromSize == 0x200) { + EepString = "4K OK!"; + } else if (gEepromSize == 0x800) { + EepString = "16K OK!"; + } + if (gCICType == CIC_TYPE_INVALID) { + CICString = Failed; + } else if (gCICType == CIC_TYPE_PAL) { + CICString = PAL; + } else if (gCICType == CIC_TYPE_NTSC) { + CICString = NTSC; + } + + sprintf(buf, + "\nCart tester report:\n\n" + " EEPROM - %s\n" + " SRAM - %s\n" + " FlashRam - %s\n" + " CIC - %s %s\n" + " Romsize - %luMB\n" + " RomName - %s\n" + " RomID - %04X %c%c\n" + " CartType - %c\n" + " RomRegion - %c\n" + " RomVersion - %02X\n", + EepString, + (gSRAMPresent != 0) ? OK : NotPresent, + (gFramPresent != 0) ? OK : NotPresent, + CICString, + gCICName, + (gRomSize / (1024 * 1024)), + (char*)gGameTitle, + gGameCode[1], ((gGameCode[1] >> 8) & 0xFF), (gGameCode[1] & 0xFF), + gGameCode[0] & 0xFF, + ((gGameCode[2] >> 8) & 0xFF), + (gGameCode[2] & 0xFF) + ); + } else { + memset(buf, 0, SECTOR_SIZE); + } } else if (cluster == EEPROMFLIP_CLUSTER_START) { uint32_t address = (((uint32_t)cluster - (EEPROMFLIP_CLUSTER_START)) * CLUSTER_SIZE) + (cluster_offset * SECTOR_SIZE); ReadEepromData(address / 64, buf); for (uint32_t i = 0; i < 256; i += 1) { - ((uint16_t*)buf)[i] = flip8(((uint16_t*)buf)[i]); + ((uint16_t*)buf)[i] = flip16(((uint16_t*)buf)[i]); } } else if (cluster >= FLASHRAMFLIP_CLUSTER_START) { // Read SRAM/FRAM -- check if the cart responds to Flashram info request first, if not treat as SRAM. @@ -461,7 +525,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf, set_address(address); for (uint32_t i = 0; i < 256; i += 1) { - ((uint16_t*)buf)[i] = flip8(read16()); + ((uint16_t*)buf)[i] = flip16(read16()); } } else if (cluster >= Z64ROM_CLUSTER_START) { // Read Z64 rom @@ -469,7 +533,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf, address += 0x10000000; set_address(address); for (uint32_t i = 0; i < 256; i += 1) { - ((uint16_t*)buf)[i] = flip8(read16()); + ((uint16_t*)buf)[i] = flip16(read16()); } } else if (cluster >= N64ROM_CLUSTER_START) { // Read N64 rom