-
Notifications
You must be signed in to change notification settings - Fork 34
3) The 64Drive
This page describes how the UNFLoader tool and USB library handles the 64Drive flash cart.
The 64Drive is a flashcart developed by marshallh and sold on his retroactive website. It features a very fast 8MB/s USB FTDI chip, and very extensive documentation (Denoted hereafter as "hardware specification" or "hardware spec"). The 64Drive's USB protocol only works if the cart is running firmware versions 2.05 and above. The 64Drive is available in two different hardware revisions, Hardware 1 and Hardware 2 (Denoted hereafter as "HW1" and "HW2" respectively)
On the 64Drive, reads from cartridge space are mapped to SDRAM, meaning that you can use the osPiRead and osPiWrite functions to address the different cartridge interface devices.
HW1 runs in Asynchronous mode while HW2 runs in Synchronous. For USB communication, HW2 features a mode called "Extended Address", which allows the 64Drive to support ROMs up to 256MB in size. This functionality, while convenient (since ROM space would not be eaten by the USB buffers), has not been put to use in UNFLoader.
After probing the connected devices with FT_GetDeviceInfoList, it is a matter comparing the Description and ID values of the FT_DEVICE_LIST_INFO_NODE to see if they match up with any of the following:
| Description (String) | ID (DWORD) | |
|---|---|---|
| HW1 | 64drive USB device A | 0x4036010 |
| HW2 | 64drive USB device | 0x4036014 |
Simply call osPiReadIo, reading from the Hardware Magic value described in the Registers table on page 2 of the hardware spec. The connected device is a 64Drive if the returned value is equivalent to 0x55444556.
Open the deivce using FT_Open, resetting the device with FT_ResetDevice and setting it's timeouts to a large enough value (by default, 5000 miliseconds). If the HW2 is being used, synchronous mode is set by using FT_SetBitMode on FT_BITMODE_RESET and FT_BITMODE_SYNC_FIFO with mask value of 0xFF. Finally, it is recommended to purge the receive and transmission buffers with the use of the FT_Purge command with the mask FT_PURGE_RX | FT_PURGE_TX.
As explained in the hardware spec, the 64Drive uses a command based protocol for communication. Everything you need to know about the available commands are already explained there on pages 15 to 21.
To upload a ROM, the N64 must be turned off and the USB must be connected. The 64Drive does not set the CIC and savetypes by default, and therefore these must be sent with the 0x72 and 0x70 commands respectively. The 0x20 command is then used to upload the ROM itself.
In order to send data to the connected PC, follow the instructions outlines in page 14 of the hardware specification document. For the Block Type value mentioned in Param1/Result1, simply use one of the DATATYPE values described in the previous chapter. The data sent by the 64Drive will be automatically formatted in the communication protocol used by UNFLoader (IE the 64Drive will send DMA@ followed by the data header for you, meaning you will not need to append this data manually into what you send through USB).
On page 13 of the hardware spec, the process for arming the USB FIFO to receive data from the PC is explained, and in page 14 it is shown how to disarm the USB FIFO. However the arming process described in the document is wrong. This, I believe, is due to a bug in the firmware, however I cannot confirm it as marshallh has been difficult to reach.
The hardware spec claims that you can arm the buffer with any size you want, so long as you repeatedly rearm the USB to service the rest of the incoming data. However, in practice, the 64Drive will lock up if the size of the data is larger than the armed buffer.
Another thing the hardware spec claims is that the armed buffer can by any size larger than 8MB, and while this is true, in practice, any data that is larger than 512 bytes (which is the internal USB buffer's size) has a high chance to get mangled (bytes can go missing, swap places, etc...). Therefore, the following workaround has been used by UNFLoader. It's slow, it can sometimes completely miss incoming data, but it is better than nothing...
First, send 8 bytes to the N64 containing DMA@ and the data header, so that the USB library can know that it is going to receive data, what type of data, and the size of the data. Then, send the data in 512 byte blocks calling the write command (0x40) for each block. This means that all data will always be sent to the first 512 byte position in the armed ROM buffer, overwriting one another. The USB library will automatically offset this data to the correct position in ROM. However, because the first block of data will always be overwritten by the next, you must send the data backwards (IE, send the last block first, penultimate block after, etc... until you send the first block last). There is no need to send a final CMPH block at the end, this was omitted as it would overwrite the first block of data, which would be an annoying edge case to deal with...
If you try running example 3 on the 64Drive, you might notice that half or more of the things you send do not get echoed back, but example 4 works perfectly fine.
Something which isn't explained in the hardware specification (possibly due to a firmware bug) is that the 64Drive will eventually lock up when armed and left running. For UNFLoader, I decided to arm the 64Drive, check if data was received, and then disarm it right after. Because this is done so quickly, the 64Drive can sometimes miss or drop the data... This problem only arises if the polling function is called too quickly, but will not be a problem for games running multiple threads.
A more correct approach would be to have a small delay before disarming (but would cause the games to slow down slightly since it has to check if it received data for a short period of time). However because most people won't be using the library in a bare, single threaded application like the example 3, I thought it was fine as is.