Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/famicom-dumper-writer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'FamicomDumper/Core/Src/fds.c')
-rw-r--r--FamicomDumper/Core/Src/fds.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/FamicomDumper/Core/Src/fds.c b/FamicomDumper/Core/Src/fds.c
new file mode 100644
index 0000000..07303f0
--- /dev/null
+++ b/FamicomDumper/Core/Src/fds.c
@@ -0,0 +1,362 @@
+#include "main.h"
+#include "dumper.h"
+#include "comm.h"
+#include "led.h"
+
+static volatile uint8_t dummy;
+
+// Transfer single byte
+// output - pointer to output buffer
+// input - input byte
+// *end_of_head - writes non zero if end of head meet
+// Returns 1 on success, negative error code on error
+static int8_t transfer_fds_byte(uint8_t *output, uint8_t input, uint8_t *end_of_head)
+{
+ uint32_t start_time;
+ start_time = HAL_GetTick();
+ while (!IRQ_FIRED)
+ {
+ // waiting for interrupt
+ // timeout 5 secs
+ if (HAL_GetTick() - start_time >= 5000)
+ return -COMMAND_FDS_TIMEOUT;
+ }
+ if (output)
+ *output = PRG(FDS_DATA_READ);
+ else
+ dummy = PRG(FDS_DATA_READ);
+ PRG(FDS_DATA_WRITE) = input; // clear interrupt
+ uint8_t disk_status = PRG(FDS_DISK_STATUS);
+ if (end_of_head)
+ *end_of_head |= (disk_status >> 6) & 1;
+ start_time = HAL_GetTick();
+ while (IRQ_FIRED)
+ {
+ // is interrupt flag cleared?
+ // timeout 5 secs
+ if (HAL_GetTick() - start_time >= 5000)
+ return -COMMAND_FDS_TIMEOUT;
+ }
+ return 1;
+}
+
+// Function to read single block
+// length - amount of bytes to read
+// send - non-zero if need to transfer data over USB
+// file_size - pointer to store file size extracted from file header
+// gap_delay - gap length
+// Returns 1 on success, negative error code on error
+static int8_t read_fds_block_send(uint16_t length, uint8_t send, uint16_t *file_size, uint32_t gap_delay)
+{
+ uint8_t data;
+ uint8_t disk_status;
+ uint32_t b;
+ uint8_t crc_ok = 1;
+ uint8_t end_of_head = 0;
+ int8_t r;
+
+ delay_clock(gap_delay);
+ if (send)
+ {
+ led_green();
+ comm_start(COMMAND_FDS_READ_RESULT_BLOCK, length + 2);
+ }
+ // start transfer, enable IRQ
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON;
+ for (b = 0; b < length; b++)
+ {
+ r = transfer_fds_byte(&data, 0, &end_of_head);
+ // handle errors
+ if (r <= 0)
+ {
+ if (!r) return r;
+ // send empty rest of the packet
+ // before sending error code
+ if (send)
+ for (;b < length + 2; b++)
+ if (!comm_send_byte(0))
+ return 0;
+ return r;
+ }
+ // parse file size if need
+ if (file_size)
+ {
+ if (b == 13)
+ *file_size = data;
+ else if (b == 14)
+ *file_size |= data << 8;
+ }
+ // send data over USB if need
+ if (send)
+ {
+ if (!comm_send_byte(data)) return 0;
+ }
+ }
+ r = transfer_fds_byte(0, 0, &end_of_head);
+ if (r <= 0)
+ {
+ if (send)
+ {
+ if (!comm_send_byte(0)) return 0;
+ if (!comm_send_byte(0)) return 0;
+ }
+ return r;
+ }
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON | FDS_CONTROL_CRC; // enable CRC control
+ r = transfer_fds_byte(0, 0, &end_of_head);
+ if (r <= 0)
+ {
+ if (send)
+ {
+ if (!comm_send_byte(0)) return 0;
+ if (!comm_send_byte(0)) return 0;
+ }
+ return r;
+ }
+ disk_status = PRG(FDS_DISK_STATUS);
+ crc_ok &= ((disk_status >> 4) & 1) ^ 1;
+ end_of_head |= (disk_status >> 6) & 1;
+ if (send)
+ {
+ if (!comm_send_byte(crc_ok)) return 0; // CRC result
+ if (!comm_send_byte(end_of_head)) return 0; // end of head meet?
+ }
+ if (send && (!crc_ok || end_of_head)) return -COMMAND_FDS_READ_RESULT_END;
+ if (!crc_ok) return -COMMAND_FDS_BLOCK_CRC_ERROR;
+ if (end_of_head) return -COMMAND_FDS_END_OF_HEAD;
+
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON; // motor on without transfer
+
+ led_cyan();
+ return 1; // success
+}
+
+// Function to write single block
+// data - pointer to data to write
+// length - amount of bytes to write
+// gap_delay - gap length
+// Returns 1 on success, negative error code on error
+static int8_t write_fds_block(uint8_t *data, uint16_t length, uint32_t gap_delay)
+{
+ uint8_t end_of_head = 0;
+ uint32_t start_time;
+ uint16_t pos = 0;
+ int8_t r;
+
+ led_red();
+ PRG(FDS_CONTROL) = FDS_CONTROL_WRITE | FDS_CONTROL_MOTOR_ON; // enable writing without transfer
+ delay_clock(gap_delay);
+ PRG(FDS_DATA_WRITE) = 0x00; // write $00
+ // start transfer, enable IRQ
+ PRG(FDS_CONTROL) = FDS_CONTROL_WRITE | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON;
+ r = transfer_fds_byte(0, 0x80, &end_of_head); // write start bit
+ if (r <= 0) return r;
+ while (length)
+ {
+ if (end_of_head) return -COMMAND_FDS_END_OF_HEAD;
+ r = transfer_fds_byte(0, *data, &end_of_head);
+ if (r <= 0) return r;
+ data++;
+ length--;
+ pos++;
+ // avoid copy protection, lol
+ if ((pos % FDS_COPY_PROTECTION_RESET_INTERVAL) == 0)
+ {
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON;
+ PRG(FDS_CONTROL) = FDS_CONTROL_WRITE | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON;
+ }
+ }
+ r = transfer_fds_byte(0, 0xFF, &end_of_head);
+ if (r <= 0) return r;
+ if (end_of_head) return -COMMAND_FDS_END_OF_HEAD;
+ PRG(FDS_CONTROL) = FDS_CONTROL_WRITE | FDS_CONTROL_MOTOR_ON | FDS_CONTROL_TRANSFER_ON | FDS_CONTROL_IRQ_ON | FDS_CONTROL_CRC; // enable CRC control
+ delay_clock(FDS_WRITE_CRC_DELAY);
+ start_time = HAL_GetTick();
+ while (1)
+ {
+ uint8_t drive_status = PRG(FDS_DRIVE_STATUS);
+ if (!(drive_status & FDS_DRIVE_STATUS_DISK_NOT_READY))
+ break; // ready
+ // timeout 1 sec
+ if (HAL_GetTick() - start_time >= 1000)
+ {
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_OFF; // reset, stop
+ return -COMMAND_FDS_TIMEOUT;
+ }
+ }
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON; // motor on without transfer
+
+ led_cyan();
+ return 1; // success
+}
+
+// Internal read/write function
+// block_read_start - number of start block to read
+// block_read_count - amount of blocks to read
+// block_write_count - amount of blocks to write
+// block_write_ids - pointer to block numbers to write
+// write_data - pointer to arrays of blocks to write
+// Returns 1 on success, negative error code on error
+static int8_t fds_transfer_real(uint8_t block_read_start, uint8_t block_read_count, uint8_t block_write_count, uint8_t *block_write_ids, uint16_t *write_lengths, uint8_t *write_data)
+{
+ uint8_t current_block = 0;
+ uint32_t start_time;
+ uint8_t drive_status;
+ uint8_t blocks_writed = 0;
+ int8_t r;
+
+ led_magenta();
+ PRG(FDS_IRQ_CONTROL) = 0x00; // disable timer IRQ
+ PRG(FDS_MASTER_IO) = 0x01; // enable disk registers
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_OFF; // reset
+ drive_status = PRG(FDS_DRIVE_STATUS);
+ // is disk inserted?
+ if (drive_status & FDS_DRIVE_STATUS_DISK_NOT_INSERTED)
+ return -COMMAND_FDS_DISK_NOT_INSERTED;
+ // is it write protected while we are writing?
+ if (block_write_count && (drive_status & FDS_DRIVE_STATUS_DISK_WRITE_PROTECTED))
+ return -COMMAND_FDS_DISK_WRITE_PROTECTED;
+ // battery test
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_ON; // monor on, unreset
+ PRG(FDS_EXT_WRITE) = 0xFF;
+ HAL_Delay(100);
+ if (!(PRG(FDS_EXT_READ) & 0x80))
+ return -COMMAND_FDS_BATTERY_LOW;
+
+ // waiting until drive is rewinded
+ start_time = HAL_GetTick();
+ do
+ {
+ // timeout 15 secs
+ if (HAL_GetTick() - start_time >= 15000)
+ return -COMMAND_FDS_TIMEOUT;
+ } while (!(PRG(FDS_DRIVE_STATUS) & FDS_DRIVE_STATUS_DISK_NOT_READY));
+ start_time = HAL_GetTick();
+ do
+ {
+ // timeout 15 secs
+ if (HAL_GetTick() - start_time >= 15000)
+ return -COMMAND_FDS_TIMEOUT;
+ } while (PRG(FDS_DRIVE_STATUS) & FDS_DRIVE_STATUS_DISK_NOT_READY);
+
+ led_cyan();
+
+ // disk info block
+ if (block_write_count && (current_block == block_write_ids[blocks_writed]))
+ {
+ // writing disk info block
+ // gap delay while writing = ~28300 bits = (~28300 / 8)bits * ~165cycles = ~583687.5
+ uint16_t write_length = write_lengths[blocks_writed];
+ r = write_fds_block(write_data, write_length, FDS_WRITE_GAP_BEFORE_FIRST_BLOCK);
+ if (r <= 0) return r;
+ write_data += write_length;
+ blocks_writed++;
+ block_write_count--;
+ } else
+ {
+ // reading disk info block
+ // gap delay while reading = ~486974 cycles
+ r = read_fds_block_send(56, (current_block >= block_read_start) && block_read_count, 0, FDS_READ_GAP_BEFORE_FIRST_BLOCK);
+ if (r <= 0) return r;
+ }
+ if ((current_block >= block_read_start) && block_read_count)
+ block_read_count--;
+ current_block++;
+
+ // file amount block
+ if (block_read_count || block_write_count)
+ {
+ if (block_write_count && (current_block == block_write_ids[blocks_writed]))
+ {
+ // writing file amount block
+ uint16_t write_length = write_lengths[blocks_writed];
+ r = write_fds_block(write_data, write_length, FDS_WRITE_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ write_data += write_length;
+ blocks_writed++;
+ block_write_count--;
+ } else
+ {
+ // reading file amount block
+ r = read_fds_block_send(2, (current_block >= block_read_start) && block_read_count, 0, FDS_READ_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ }
+ if ((current_block >= block_read_start) && block_read_count)
+ block_read_count--;
+ current_block++;
+ }
+
+ // files
+ while (block_read_count || block_write_count)
+ {
+ // file header block
+ uint16_t file_size = 0; // size of the next block
+ if (block_write_count && (current_block == block_write_ids[blocks_writed]))
+ {
+ // writing file header block
+ uint16_t write_length = write_lengths[blocks_writed];
+ r = write_fds_block(write_data, write_length, FDS_WRITE_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ write_data += write_length;
+ blocks_writed++;
+ block_write_count--;
+ } else
+ {
+ // reading file header block
+ r = read_fds_block_send(16, (current_block >= block_read_start) && block_read_count, &file_size, FDS_READ_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ }
+ if ((current_block >= block_read_start) && block_read_count)
+ block_read_count--;
+ current_block++;
+
+ // file data block
+ if (block_read_count || block_write_count)
+ {
+ if (block_write_count && (current_block == block_write_ids[blocks_writed]))
+ {
+ // writing file data block
+ uint16_t write_length = write_lengths[blocks_writed];
+ r = write_fds_block(write_data, write_length, FDS_WRITE_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ write_data += write_length;
+ blocks_writed++;
+ block_write_count--;
+ } else
+ {
+ // reading file data block
+ r = read_fds_block_send(file_size + 1, (current_block >= block_read_start) && block_read_count, 0, FDS_READ_GAP_BETWEEN_BLOCKS);
+ if (r <= 0) return r;
+ }
+ if ((current_block >= block_read_start) && block_read_count)
+ block_read_count--;
+ current_block++;
+ }
+ }
+
+ return 1;
+}
+
+// General read/write function
+// block_read_start - number of start block to read
+// block_read_count - amount of blocks to read
+// block_write_count - amount of blocks to write
+// block_write_ids - pointer to block numbers to write
+// write_data - pointer to arrays of blocks to write
+void fds_transfer(uint8_t block_read_start, uint8_t block_read_count, uint8_t block_write_count, uint8_t *block_write_ids, uint16_t *write_lengths, uint8_t *write_data)
+{
+ int8_t r = fds_transfer_real(block_read_start, block_read_count, block_write_count, block_write_ids, write_lengths, write_data);
+ PRG(FDS_CONTROL) = FDS_CONTROL_READ | FDS_CONTROL_MOTOR_OFF; // reset, stop
+ HAL_Delay(50);
+ if (r > 0)
+ {
+ // success
+ if (block_write_count && !block_read_count)
+ comm_start(COMMAND_FDS_WRITE_DONE, 0);
+ else
+ comm_start(COMMAND_FDS_READ_RESULT_END, 0);
+ } else if (r < 0) {
+ // Handle error
+ comm_start(-r, 0);
+ }
+}